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 🙂 |
