Cómo utilizar useSelect y useDispatch en Gutenberg

28 sept. 2020
·
  • Gutenberg
  • JavaScript
  • What is code
·
  • store
  • useDispatch
  • useSelect

useSelect y useDispacth son dos hooks de React hechos específicamente para trabajar con Gutenberg dentro de cualquier componente. El primero de ellos permite acceder a cualquier almacén de estado (store) dentro de Gutenberg y el segundo facilita la actualización de algún atributo o campo del post que estamos editando. Sobre cuáles son esos stores y para qué sirve cada uno hablaremos en otro momento y nos centraremos más en el uso de estos dos hooks.

Todos los ejemplos a continuación necesitan importar useSelect o useDispatch de la librería data de WordPress de la siguiente forma:

const { useSelect, useDispatch } = wp.data;

useSelect

Con useSelect accederemos a cualquier store de Gutenberg tal y como hemos dicho. Recibe una función que devolverá cualquier cosa que permita recuperar un dato, campo o atributo del post que se está editando.

Imaginemos por ejemplo que no queremos mostrar algún componente dentro de un bloque si el tipo de post no es una página.

const postType = useSelect( callback );
if ( postType !== 'page' ) {
   return null;
}

return <div>Mi componente!</div>

Tenemos que usar useSelectde tal forma que devuelva el tipo de post y lo almacene en postType. Para ello, dentro de nuestro callback accederemos a algún store de Gutenberg donde esa información esté almacenada, en este caso es core/editor. Dicho callback recibe además una función, select, desde donde podremos acceder a cualquier store con él. Lo que retornemos será almacenado en postType.

const postType = useSelect( ( select ) => {
   return select( 'core/editor' ).getCurrentPostType();
} );
...

También podríamos optar por otra opción: En lugar de almacenar un valor string, podríamos sacar la función entera y usarla cuando queramos.

const getPostType = useSelect( ( select ) => {
   return select( 'core/editor' ).getCurrentPostType;
} );

if ( getPostType() !== 'page' ) {
   return null;
}
...

Hay una sutil diferencia entre el primer ejemplo y el segundo pero el uso que se hace de useSelect es bien distinto.

Veamos otro ejemplo. Imaginemos que queremos listar las últimas páginas publicadas en nuestro sitio. En este caso tendremos que usar el storecore que pone a nuestra disposición una función muy potente pero poco documentada llamada getEntityRecords y que hará un fetch de lo que queramos: posts, terms… Lo que sea:

const latestPages = useSelect( ( select ) => {
   return select( 'core' ).getEntityRecords( 'postType', 'page' );
} );

return latestPages && latestPages.map( ( page ) => {
   return <div>{ page.title.rendered }</div>;
} );

Esta llamada tiene una particularidad y es que si no hay nada almacenado en caché, getEntityRecordsdevolverá null la primera vez. Hay que realizar una segunda llamada para ver los resultados reales, una vez ha terminado la llamada a la REST API. Cuando creamos componentes o bloques no tiene demasiada importancia ya que el componente se actualizará una vez los datos cambien pero hay que tener en cuenta que en lugar de un array podría llegar null, por eso realizo la comprobación latestPosts &&..., en otro caso podría dar un error.

Con esta misma función podríamos crear algo más potente que recibiera parámetros para filtrar páginas:

const getLatestPages = useSelect( ( select ) => {
   return ( args ) => {
     return select( 'core' ).getEntityRecords( 'postType', 'page', args ); 
   } 
} );

const latestPages = getLatestPages( { per_page: 4, status: 'draft' } );

return latestPages && latestPages.map( ( page ) => {
   return <div>{ page.title.rendered }</div>;
} );

Ahora, useSelect retorna una función a la que podemos pasar parámetros para afinar nuestra query, de forma similar a WP_Query.

Casi todo lo que saca Gutenberg por pantalla podemos hacerlo con useSelect y aquí hay algunos ejemplos más:

// Listado de todos los bloques en el editor
select( 'core/block-editor' ).getBlocks();

// Listado de todos los bloques registrados y sus propiedades
select( 'core/blocks' ).getBlockTypes();

// Propiedades del post que se está editando
select( 'core/editor' ).getCurrentPost();

// Si se ha editado el extracto, aquí aparecerá el nuevo valor.
select( 'core/editor' ).getEditedPostAttribute( 'excerpt' );

useDispatch

De forma parecida a useSelect, useDispatchpermite acceder a cualquier store pero esta vez para actualizar algo. En lugar de recibir una función, a useDispatch le pasamos el nombre de la store.

Imaginemos ahora que tenemos un bloque o componente con un campo de texto en el que cada vez que se escribe algo, cambiará el título de la entrada, es un ejemplo un poco tonto pero al menos sirve para ilustrar.

const { TextControl } = wp.components;

...

// La función editPost puede actualizar cualquier campo del post.
// useDispatch tiene muchísimas más pero nos quedamos con esta.
const { editPost } = useDispatch( 'core/editor' );

return <TextControl
   label="Cambia el título
   onChange={ ( newTitle ) => { editPost( { title: newTitle } ) } }
/>;

Esta vez, useDispatch retorna todas las funciones del store que le digamos, en este caso sólo nos quedamos con editPost.

Al igual que con useSelect, aquí pongo algunos ejemplos más:

// Deshace el último cambio
useDispatch( 'core' ).undo(); 

// Guarda el post (como si le diéramos al botón de actualizar)
useDispatch( 'core' ).savePost()

Cómo jugar con todo esto

Crear bloques o componentes para jugar con la API de datos de Gutenberg se torna un poco aburrido de hacer si sólo queremos probar pero desde la consola de JavaScript podemos ejecutar comandos directamente. Lo podemos hacer desde cualquier instalación de WordPress pero en este sitio hay una instancia de Gutenberg pública en la que se pueden probar cosas rápidas: https://wordpress.org/gutenberg/

Como useSelect y useDispatch son hooks, sólo se pueden usar dentro de componentes de React pero se puede utilizar el objeto wp.data en la consola. Prueba a abrir el enlace y luego la consola y a continuación escribe:

wp.data.dispatch( 'core/editor' ).editPost( { title: wp.data.select( 'core/editor' ).getEditedPostAttribute( 'title' ) + ' // Hola desde igmoweb' } )

Ahora observa qué ha pasado con el título.