Contents
Introducción
En Gutenberg y en cualquier desarrollo con bloques para WordPress moderno, la librería wp.data es la forma recomendada para leer estado (select) y disparar acciones (dispatch) en los distintos stores del editor. Este artículo explica con todo detalle cómo consumir wp.data —tanto mediante las APIs React hooks (useSelect / useDispatch) como mediante la API global select/dispatch/subscribe— y muestra ejemplos prácticos para bloques JS, patrones seguros y buenas prácticas para evitar problemas de rendimiento o bucles infinitos.
Conceptos clave
- Stores: wp.data organiza estado en stores con nombres como core, core/editor, core/block-editor, core/block-editor o stores personalizados que registres.
- select(storeName): permite leer el estado (selectors). Devuelve datos de forma síncrona algunos selectors pueden estar pendientes de resolución (resolvers) y devolver undefined hasta que los datos lleguen.
- dispatch(storeName): dispara acciones (actions) que mutan el estado o ejecutan operaciones asíncronas.
- Hooks React: useSelect y useDispatch (importados desde @wordpress/data) son la forma idiomática para leer y mutar estado dentro de componentes de bloque / editor. Manejan la suscripción y re-render automáticamente.
- subscribe: permite suscribirse a cambios globales del store registry útil en scripts fuera de React o para orquestar reactividad más fina.
Cuándo usar useSelect / useDispatch vs select / dispatch
- useSelect / useDispatch: en componentes React (por ejemplo, la función edit de un bloque). Evitan manejo manual de suscripciones y re-renderizan el componente cuando cambia el selector usado.
- select / dispatch / subscribe: en scripts fuera de React, en funciones globales o cuando no exista un árbol React. También útil para testing o utilidades que se ejecutan fuera de la renderización de un componente.
Regla importante
Nunca llames a un dispatch directamente dentro de la función de renderizado de un componente (ni dentro del callback de useSelect). Si necesitas disparar una acción en respuesta a un cambio de selección, usa useEffect (o un equivalente) para evitar bucles infinitos.
Ejemplos prácticos
1) Ejemplo: leer meta del post y actualizarlo con useSelect useDispatch
Este ejemplo muestra el patrón más común: en la UI de un bloque quieres mostrar y escribir un valor de meta del post. Observa el uso de useSelect para leer y useDispatch para editar el post la actualización se hace en un handler que evita efectos durante la renderización.
// Language: javascript import { useSelect, useDispatch } from @wordpress/data import { TextControl, Button } from @wordpress/components import { useState, useEffect } from @wordpress/element export default function EditMyMetaBlock() { // Lee el meta editado en el editor const metaValue = useSelect( (select) => select(core/editor).getEditedPostAttribute(meta)?._my_meta , [] // dependencias: si quieres re-evaluar cuando cambien props/atributos, añádelas aquí ) // Obtén la acción editPost desde core/editor const { editPost } = useDispatch(core/editor) // Local state para el input para evitar escribir cada tecla directamente al store const [localValue, setLocalValue] = useState(metaValue) // Mantener localValue sincronizado cuando metaValue cambie desde otro sitio useEffect(() => { setLocalValue(metaValue) }, [metaValue]) // Handler que aplica el cambio al post (no dentro del render) function onApply() { editPost({ meta: { _my_meta: localValue } }) } return ( <>setLocalValue(v)} /> > ) }
2) Ejemplo: manipular bloques en el editor (insertar, reemplazar, seleccionar)
Para manipular bloques dentro del editor usamos el store core/block-editor. Aquí se muestra cómo insertar un bloque programáticamente, reemplazar un bloque y seleccionar un bloque.
// Language: javascript import { useSelect, useDispatch } from @wordpress/data import { Button } from @wordpress/components import { createBlock } from @wordpress/blocks export default function BlockControlsExample() { const blocks = useSelect((select) => select(core/block-editor).getBlocks(), []) const { insertBlocks, replaceBlocks, selectBlock } = useDispatch(core/block-editor) function addParagraph() { const paragraphBlock = createBlock(core/paragraph, { content: Bloque insertado programáticamente }) insertBlocks(paragraphBlock, undefined / index al final /) } function replaceFirst() { if (!blocks.length) return const newBlock = createBlock(core/heading, { content: Reemplazo de bloque }) replaceBlocks(blocks[0].clientId, newBlock) // seleccionar el nuevo bloque: selectBlock espera clientId replaceBlocks devuelve clientId? // Si necesitas seleccionar explícitamente, obtén después el bloque insertado o usa callbacks/efectos. } function selectFirst() { if (!blocks.length) return selectBlock(blocks[0].clientId) } return ( <> > ) }
3) Ejemplo: uso de select / dispatch / subscribe fuera de React
Puede que necesites ejecutar lógica desde un archivo JS que no es componente React (por ejemplo, un script cargado en admin). En ese caso usa wp.data.select/dispatch y wp.data.subscribe para escuchar cambios.
// Language: javascript // Este código asume wp.data disponible globalmente (en el editor) (function () { const { select, dispatch, subscribe } = wp.data // Leer título actual console.log(Título actual:, select(core/editor).getEditedPostAttribute(title)) // Escuchar cambios y reaccionar una sola vez let lastTitle = select(core/editor).getEditedPostAttribute(title) const unsubscribe = subscribe(() =gt { const newTitle = select(core/editor).getEditedPostAttribute(title) if (newTitle !== lastTitle) { lastTitle = newTitle console.log(Título cambiado a:, newTitle) // Si queremos, disparar acción al cambiar: // dispatch(core/editor).editPost({ meta: { _some: value } }) } }) // Para dejar de escuchar cuando sea necesario: // unsubscribe() })()
4) Patrón con withSelect / withDispatch (HOC) — legado
Si estás manteniendo código que usa HOCs en lugar de hooks, aquí tienes un patrón con withSelect y withDispatch. Funciona bien cuando no puedes usar hooks (por ejemplo, clases).
// Language: javascript import { withSelect, withDispatch } from @wordpress/data import { compose } from @wordpress/compose import { TextControl } from @wordpress/components function MyBlockEdit(props) { const { metaValue, editPostMeta } = props return () } export default compose( withSelect((select) =gt { return { metaValue: select(core/editor).getEditedPostAttribute(meta)?._my_meta , } }), withDispatch((dispatch) =gt { return { editPostMeta: (value) =gt dispatch(core/editor).editPost({ meta: { _my_meta: value } }), } }) )(MyBlockEdit)
Trucos y detalles avanzados
- Datos que tardan en resolverse: muchos selectors (por ejemplo, getEntityRecords) dependen de resolvers que realizan peticiones a la REST API. Cuando uses useSelect, el selector puede devolver undefined inicialmente. Trata undefined como loading y renderiza un placeholder o spinner hasta que llegue el dato.
- Evitar re-renderes innecesarios: pasa una función selector estable a useSelect y limita dependencias. useSelect re-ejecuta cuando cambia la salida del selector. Usa useCallback / useMemo si calculas funciones derivadas costosas.
- No llamar dispatch durante render: si necesitas actualizar el store cuando la selección cambie, usa useEffect para disparar la acción en respuesta al cambio.
- Batching: muchas acciones pueden agruparse en una única llamada a editPost cuando sea posible para evitar múltiples renders.
- Subscripciones: si usas wp.data.subscribe, asegúrate de llamar a unsubscribe cuando ya no necesites la suscripción (por ejemplo, al desmontar un componente o cuando el script finaliza).
- Store registry: si necesitas trabajar con un store personalizado, regístralo con wp.data.registerStore y utilízalo por su nombre en select/dispatch. El patrón es el mismo.
Ejemplo completo y seguro: leer posts con getEntityRecords y mostrar loading
Aquí un patrón típico para obtener entidad (posts) desde core y manejar la condición de carga:
// Language: javascript import { useSelect } from @wordpress/data import { Spinner } from @wordpress/components export default function MyPostsList() { const posts = useSelect( (select) =gt select(core).getEntityRecords(postType, post, { per_page: 5 }), [] // puedes añadir dependencias si cambias filtros dinámicamente ) if (posts === undefined) { // El selector aún se está resolviendo return ltSpinner /gt } if (!posts posts.length === 0) { return ltpgtNo hay posts.lt/pgt } return ( ltulgt {posts.map((post) =gt ( ltli key={post.id}gt{post.title.rendered}lt/ligt ))} lt/ulgt ) }
Buenas prácticas resumidas
- Usa useSelect/useDispatch dentro de componentes React y select/dispatch fuera de React.
- No dispares dispatch dentro del render: usa useEffect para reacciones a cambios.
- Maneja selectors que devuelven undefined (loading) con placeholders.
- Minimiza los selectores que consumes combinar datos en un solo selector puede reducir re-renderizados.
- Usa createBlock y las funciones del store core/block-editor para operaciones sobre bloques (insertBlocks, replaceBlocks, selectBlock, updateBlockAttributes si aplica).
- Si trabajas con stores personalizados, registra correctamente resolvers y actions para integrarlo con la arquitectura de wp.data.
Referencias rápidas (APIs comunes)
- Hooks: useSelect((select) =gt …), useDispatch(storeName)
- Global: const { select, dispatch, subscribe } = wp.data
- Stores frecuentes: core, core/editor, core/block-editor, core/block-editor (tareas relacionadas con el editor y bloques)
- Funciones útiles: select(…).getEditedPostAttribute(meta), select(core/block-editor).getBlocks(), dispatch(core/editor).editPost({…}), dispatch(core/block-editor).insertBlocks(…)
Conclusión
wp.data es una herramienta potente y flexible para interactuar con el estado del editor de WordPress. Usar correctamente select/dispatch y las abstracciones React (useSelect/useDispatch) permite construir bloques robustos y con buen rendimiento. Recuerda manejar estados de carga, evitar dispatchs en render, y optar por patrones que minimicen re-renderizados. Los ejemplos incluidos cubren los casos más habituales: meta del post, manipulación de bloques, uso fuera de React y compatibilidad con HOCs.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |