Este artículo explica, paso a paso y con todo lujo de detalles, cómo registrar y usar un store personalizado en WordPress usando @wordpress/data (JavaScript). Incluye explicación del patrón, ejemplos prácticos de código que puedes copiar, uso desde componentes React (hooks de @wordpress/data), y una pequeña guía sobre buenas prácticas y depuración. El título principal lo pones tú el texto que sigue está listo para publicarse tal cual.
Contents
Introducción y motivación
El sistema de datos de WordPress (@wordpress/data) permite centralizar el estado de tu plugin o bloque de Gutenberg, exponer selectores y acciones, y conectar componentes React con selectores y dispatchers. Registrar un store personalizado te da control total sobre el estado, facilita la reutilización entre componentes y mejora la testabilidad.
Qué necesitas
- WordPress con soporte para bloques/Gutenberg (o un entorno frontend que cargue los scripts WP).
- Las dependencias de JS estándar: @wordpress/data y @wordpress/api-fetch (si vas a llamar a la REST API desde el cliente).
- Conocimiento básico de Redux/acciones/reductores o al menos de patrones unidireccionales de flujo de datos.
Conceptos clave
- registerStore: función para registrar un store con nombre y objetos: reducer, actions, selectors, controls, resolvers.
- Reducer: función pura que transforma el estado en respuesta a acciones.
- Actions: creadores de acciones (objetos JS) que describen cambios. Con @wordpress/data las acciones deben ser serializables (normalmente objetos).
- Selectors: funciones que leen porciones del estado (interfaz pública del store).
- Controls / Resolvers (opcionales): mecanismos para asíncronía y caching en el sistema de datos WP. En este tutorial usaremos el patrón directo más sencillo para asíncronía (llamar apiFetch y despachar acciones).
Ejemplo completo: registrar un store simple y consumirlo
Vamos a crear un store llamado my-plugin/items que mantenga un array de items, un flag de carga y un error. Primero el código del store (archivo JS que registrarás en tu bundle):
import { registerStore } from @wordpress/data import apiFetch from @wordpress/api-fetch const DEFAULT_STATE = { items: [], isLoading: false, error: null, } const ACTION_TYPES = { REQUEST_ITEMS: REQUEST_ITEMS, RECEIVE_ITEMS: RECEIVE_ITEMS, RECEIVE_ERROR: RECEIVE_ERROR, ADD_ITEM: ADD_ITEM, } // Actions: simples creadores de objetos export const actions = { requestItems() { return { type: ACTION_TYPES.REQUEST_ITEMS } }, receiveItems( items ) { return { type: ACTION_TYPES.RECEIVE_ITEMS, items } }, receiveError( error ) { return { type: ACTION_TYPES.RECEIVE_ERROR, error } }, addItem( item ) { return { type: ACTION_TYPES.ADD_ITEM, item } }, } // Reducer: maneja el estado según el tipo de acción export function reducer( state = DEFAULT_STATE, action ) { switch ( action.type ) { case ACTION_TYPES.REQUEST_ITEMS: return { ...state, isLoading: true, error: null } case ACTION_TYPES.RECEIVE_ITEMS: return { ...state, items: action.items, isLoading: false, error: null } case ACTION_TYPES.RECEIVE_ERROR: return { ...state, isLoading: false, error: action.error } case ACTION_TYPES.ADD_ITEM: return { ...state, items: [ ...state.items, action.item ] } default: return state } } // Selectors: interface pública para leer estado export const selectors = { getItems( state ) { return state.items }, isLoading( state ) { return state.isLoading }, getError( state ) { return state.error }, } // Registrar el store en el registry de @wordpress/data registerStore( my-plugin/items, { reducer, actions, selectors, } )
Explicación del ejemplo
- El store se registra bajo la key my-plugin/items. Es importante usar un namespace único para evitar colisiones.
- Las actions exportadas son funciones que devuelven objetos plain JS (tipo Redux).
- El reducer aplica las transformaciones y devuelve el nuevo estado.
- Los selectors exponen funciones para consultar la parte que interesa del estado.
Cómo realizar peticiones asíncronas (REST API) y actualizar el store
La forma más clara y robusta es usar apiFetch (o fetch) en una función utilitaria que despache las actions apropiadas al store. No es obligatorio usar controls/resolvers, y para muchos casos esta aproximación es suficiente y más explícita.
// helpers.js import apiFetch from @wordpress/api-fetch / Carga items desde la REST API y despacha acciones al store. Asume que el store my-plugin/items ya está registrado. / export function loadItemsFromAPI() { const { dispatch } = wp.data // o import { dispatch } from @wordpress/data const store = my-plugin/items // Indicar que iniciamos la carga dispatch( store ).requestItems() return apiFetch( { path: /my-plugin/v1/items } ) .then( ( items ) => { // Al resolver, actualizamos el store con los items recibidos dispatch( store ).receiveItems( items ) return items } ) .catch( ( err ) => { // En caso de error, guardar el mensaje const message = err err.message ? err.message : String( err ) dispatch( store ).receiveError( message ) throw err } ) }
Notas sobre la función de carga
- Usamos dispatch( store ).actionName() para despachar la action correspondiente del store.
- apiFetch debe estar configurado (WordPress inyecta nonce, url, etc., cuando se carga correctamente).
- La función devuelve la promesa para que quien la llame pueda encadenar comportamientos (útil en componentes).
Consumir el store desde un componente React (hooks)
En componentes de bloques o scripts frontend puedes usar useSelect y useDispatch para leer y modificar el store. Aquí un ejemplo sencillo de componente que muestra los items y carga si están vacíos:
import { useEffect } from @wordpress/element import { useSelect } from @wordpress/data import { loadItemsFromAPI } from ./helpers export default function ItemsList() { // Leer desde el selector del store const items = useSelect( ( select ) => select( my-plugin/items ).getItems(), [] ) const isLoading = useSelect( ( select ) => select( my-plugin/items ).isLoading(), [] ) const error = useSelect( ( select ) => select( my-plugin/items ).getError(), [] ) // Efecto para cargar items al montar (si es necesario) useEffect( () => { if ( items.length === 0 ) { loadItemsFromAPI().catch( () => { // El error ya queda reflejado en el store aquí podrías hacer logging adicional. } ) } }, [] ) if ( isLoading ) { returnLoading…} if ( error ) { returnError: { error }} return (
-
{ items.map( ( item ) => (
- { item.title } ) ) }
Despachar acciones directamente
Si necesitas modificar el estado desde cualquier sitio (no solo React), usa dispatch con el nombre del store y el nombre de la action:
// Añadir un item desde cualquier script const newItem = { id: 123, title: Nuevo elemento } wp.data.dispatch( my-plugin/items ).addItem( newItem )
Registrar endpoints REST en PHP (opcional)
Para servir datos desde el servidor a través de la REST API, registra un endpoint en PHP. Ejemplo mínimo:
add_action( rest_api_init, function () { register_rest_route( my-plugin/v1, /items, array( methods => GET, callback => function ( WP_REST_Request request ) { // Aquí devolvemos datos reales. Ejemplo: return rest_ensure_response( array( array( id => 1, title => Primero ), array( id => 2, title => Segundo ), ) ) }, permission_callback => __return_true, ) ) } )
Buenas prácticas y recomendaciones
- Nombres únicos: usa nombres de store con prefijo de plugin (ej. mi-plugin/items).
- Actions simples: mantenlas plain objects y serializables.
- Asíncronía explícita: manejar las llamadas a la REST API desde helpers que despachen actions (más simple y explícito que complicar controls y resolvers si no los necesitas).
- Selectors puros: que no produzcan efectos secundarios.
- Evita duplicación: centraliza la lógica de fetch en helpers para reusar caching/errores.
- Testabilidad: registra el store en tests y resetea el registry después de cada prueba si fuera necesario.
Depuración y herramientas
- Usa wp.data.select( my-plugin/items ) en la consola para inspeccionar selectores.
- En desarrollo, console.log desde reducers/actions/resolvers para entender el flujo.
- Si necesitas funcionalidades avanzadas (controls/resolvers/autocaching), revisa la documentación oficial de @wordpress/data para patrones avanzados.
Patrones avanzados (resumen)
WordPress proporciona mecanismos más complejos para automatizar la resolución de recursos (resolvers) y controles que manejan promesas de forma integrada. Úsalos si quieres que los selectores disparen automáticamente fetchs cuando detectan falta de datos. Sin embargo, para la mayoría de plugins la aproximación descrita (helpers dispatch) es suficiente y más fácil de mantener.
Checklist para implementar en tu plugin
- Crear y exportar el archivo del store (reducer, actions, selectors) y registrarlo con registerStore.
- Implementar helpers de carga que usen apiFetch y despachen actions.
- Consumir el store desde componentes con useSelect y, cuando sea necesario, con useEffect para disparar la carga.
- Registrar (si procede) el endpoint REST en PHP para servir los datos.
- Probar y depurar usando la consola y pruebas unitarias si aplica.
Conclusión
Registrar un store personalizado con @wordpress/data ofrece una forma estructurada y reutilizable de gestionar estado en tus plugins y bloques. El patrón mostrado (store helpers asíncronos hooks) es práctico, claro y mantiene la lógica de carga separada de la lógica de estado. Si en un futuro necesitas caching automático o más abstracción, puedes explorar controls y resolvers, pero empieza por esta solución sencilla y robusta.
Recursos oficiales
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |