I’ve built a Featured Gallery component for Gutenberg that uses the withSelect
and withDispatch
higher order components to store the selected images in a meta field.
I’m wondering if this code can be converted to use the useSelect
and useDispatch
hooks instead to simplify it. Is it possible? I tried to do it myself reading the almost useless Gutenberg docs with no success.
Any help would be appreciated.
/**
* WordPress dependencies
*/
const { __ } = wp.i18n;
const {
BaseControl,
Button
} = wp.components;
const { compose } = wp.compose;
const { withSelect, withDispatch } = wp.data;
const { MediaUpload, MediaUploadCheck } = wp.blockEditor;
/**
* Internal dependencies
*/
import FeaturedGalleryImage from './featured-gallery-image';
const ALLOWED_MEDIA_TYPES = [ 'image' ];
function FeaturedGallery( {
featuredGalleryIds,
onUpdateGallery,
onClearGallery,
noticeUI,
} ) {
const instructions = (
<p>
{ __(
'To edit the featured gallery, you need permission to upload media.', 'my-featured-gallery'
) }
</p>
);
const hasImages = !! featuredGalleryIds.length;
return (
<BaseControl
className="my-featured-gallery"
>
{ noticeUI }
<div className="editor-post-featured-gallery">
<div className="editor-post-featured-gallery__container">
{ hasImages && (
<ul className="featured-gallery-grid">
{ featuredGalleryIds.map( ( img ) => (
<li key={ img } tabIndex={0}>
<FeaturedGalleryImage
id={ img }
/>
</li>
) ) }
</ul>
) }
</div>
<MediaUploadCheck fallback={ instructions }>
<MediaUpload
title={ __( 'Featured gallery', 'my-featured-gallery' ) }
multiple
gallery
addToGallery={ hasImages }
onSelect={ onUpdateGallery }
allowedTypes={ ALLOWED_MEDIA_TYPES }
value={ hasImages ? featuredGalleryIds : [] }
render={ ( { open } ) => (
<Button
className={
hasImages
? 'editor-post-featured-gallery__edit'
: 'editor-post-featured-gallery__add'
}
onClick={ open }
isSecondary
>
{
hasImages
? __( 'Edit gallery', 'my-featured-gallery' )
: __( 'Add to gallery', 'my-featured-gallery' )
}
</Button>
) }
/>
</MediaUploadCheck>
{ hasImages && (
<MediaUploadCheck>
<Button onClick={ onClearGallery } isLink isDestructive>
{
__( 'Clear gallery', 'my-featured-gallery' )
}
</Button>
</MediaUploadCheck>
) }
</div>
</BaseControl>
);
}
const applyWithSelect = withSelect( ( select ) => {
const { getPostType } = select( 'core' );
const { getCurrentPostId, getEditedPostAttribute } = select(
'core/editor'
);
const meta = getEditedPostAttribute( 'meta' );
const featuredGalleryIds = meta._featured_gallery;
return {
currentPostId: getCurrentPostId(),
postType: getPostType( getEditedPostAttribute( 'type' ) ),
featuredGalleryIds,
};
} );
const applyWithDispatch = withDispatch( ( dispatch ) => {
const { editPost } = dispatch( 'core/editor' );
return {
onUpdateGallery( images ) {
const items = images.map( ( item ) => item.id );
const meta = { _featured_gallery: items };
editPost( { meta } );
},
onClearGallery() {
const meta = { _featured_gallery: [] };
editPost( { meta } );
}
};
}
);
export default compose(
applyWithSelect,
applyWithDispatch,
)( FeaturedGallery );
1 Answer
After banging my head against Gutenberg’s wall for a few hours, I came up with something that seems to work. It uses the useSelect
and useEntityProp
hooks instead of the withSelect
and withDispatch
HOCs. The truth is that using hooks makes the code simpler and easier to understand.
/**
* WordPress dependencies
*/
const { __ } = wp.i18n;
const {
BaseControl,
Button
} = wp.components;
const { useSelect } = wp.data;
const { useEntityProp } = wp.coreData;
const { MediaUpload, MediaUploadCheck } = wp.blockEditor;
/**
* Internal dependencies
*/
import FeaturedGalleryImage from './featured-gallery-image';
const ALLOWED_MEDIA_TYPES = [ 'image' ];
function FeaturedGallery( {
noticeUI,
} ) {
const instructions = (
<p>
{ __(
'To edit the featured gallery, you need permission to upload media.', 'my-featured-gallery'
) }
</p>
);
const postType = useSelect(
( select ) => select( 'core/editor' ).getCurrentPostType(),
[]
);
const [ meta, setMeta ] = useEntityProp(
'postType',
postType,
'meta'
);
const featuredGalleryIds = meta['_featured_gallery'];
const hasImages = !! featuredGalleryIds.length;
function onUpdateGallery( images ) {
const items = images.map( ( item ) => item.id );
setMeta( { ...meta, '_featured_gallery': items } );
}
function onClearGallery() {
setMeta( { ...meta, '_featured_gallery': [] } );
}
return (
<BaseControl
className="my-featured-gallery"
>
{ noticeUI }
<div className="editor-post-featured-gallery">
<div className="editor-post-featured-gallery__container">
{ hasImages && (
<ul className="featured-gallery-grid">
{ featuredGalleryIds.map( ( img ) => (
<li key={ img } tabIndex={0}>
<FeaturedGalleryImage
id={ img }
/>
</li>
) ) }
</ul>
) }
</div>
<MediaUploadCheck fallback={ instructions }>
<MediaUpload
title={ __( 'Featured gallery', 'my-featured-gallery' ) }
multiple
gallery
addToGallery={ hasImages }
onSelect={ onUpdateGallery }
allowedTypes={ ALLOWED_MEDIA_TYPES }
value={ hasImages ? featuredGalleryIds : [] }
render={ ( { open } ) => (
<Button
className={
hasImages
? 'editor-post-featured-gallery__edit'
: 'editor-post-featured-gallery__add'
}
onClick={ open }
isSecondary
>
{
hasImages
? __( 'Edit gallery', 'my-featured-gallery' )
: __( 'Add to gallery', 'my-featured-gallery' )
}
</Button>
) }
/>
</MediaUploadCheck>
{ hasImages && (
<MediaUploadCheck>
<Button onClick={ onClearGallery } isLink isDestructive>
{
__( 'Clear gallery', 'my-featured-gallery' )
}
</Button>
</MediaUploadCheck>
) }
</div>
</BaseControl>
);
}
export default FeaturedGallery;