La semana pasada vimos cómo en realidad un componente React no es más que la representación gráfica de (parte de) los datos de nuestra aplicación. En el ejemplo que implementamos, un componente mostraba el valor de un contador junto con dos botones que permitían incrementarlo o decrementarlo. El «secreto» estaba en descubrir que las props
que recibía ese componente no tenían por qué ser únicamente datos; también podían ser funciones.
Hasta el momento, pues, las dos ideas principales que hemos aprendido con la parte 1 y la parte 2 de este tutorial son:
- Un componente React es una función pura que recibe un conjunto de propiedades y genera el código HTML para «pintarlo» en nuestra página web.
- La propiedades que recibe pueden ser (a) los datos que debemos mostrar en la interfaz o (b) las funciones que, conectándolas como callbacks a los eventos del DOM, nos permitirán modificar esos datos.
Pues bien, en esta tercera parte vamos a expandir un poco más nuestros conocimientos sobre esta arquitectura y vamos a aprender a usar los stores de WordPress. Con ellos, podremos definir el estado de nuestra aplicación de una forma limpia, ordenada, testeable e independiente de los componentes gráficos que usaremos para visualizarlo.
Expandiendo nuestra aplicación de ejemplo
En la segunda parte de este tutorial creamos un contador con un par de botones para incrementar o decrementar su valor. Para ello, implementamos una pequeña chapuza en la que el valor del contador era una variable JavaScript que teníamos tirada en el fichero principal index.js
de nuestro plugin. El componente Counter era algo tal que así:
export const Counter = ( { value, onIncrease, onDecrease } ) => (
<div>
<div>Counter: <strong>{ value }</strong></div>
<button onClick={ onIncrease }>+</button>
<button onClick={ onDecrease }>-</button>
</div>
);
y el fichero principal, donde guardábamos el valor del contador y donde renderizábamos el componente, era así:
// Import dependencies
import { render } from '@wordpress/element';
import { Counter } from './components/counter';
// Store value
let value = 0;
function setValue( newValue ) {
value = newValue;
}
// Render component in DOM
const wrapper = document.getElementById( 'react-example-wrapper' );
render(
<Counter
value={ value }
onIncrease={ () => setValue( value + 1 ) }
onDecrease={ () => setValue( value - 1 ) }
/>,
wrapper
);
Pues bien, hoy vamos a retomar ese ejemplo y lo vamos a complicar un poquito más. En concreto, hoy implementaremos el store WordPress de una aplicación que nos permita añadir (y quitar) tantos contadores como queramos, cada uno de ellos con sus propios controles para incrementar y decrementar sus respectivos valores. El resultado final (que conseguiremos en la próxima entrada) será algo parecido a esto:

Definiendo el estado de nuestra aplicación
Cuando nos enfrentamos a un problema como el que te estoy planteando hoy, lo primero que debes pensar son las funciones que te permiten consultar y modificar el estado de tu aplicación. En nuestro caso, teniendo en cuenta el tipo de aplicación que te he descrito, las operaciones que necesitamos son:
- Añadir un nuevo contador
- Eliminar un contador x
- Establecer el valor de un contador x
- Ver qué contadores tengo
- Ver el valor de un contador x
Una vez tienes definida la interfaz, debes pensar en la información necesaria para mantener el estado de tu aplicación. En nuestro caso, esto es bastante fácil: necesitamos una estructura que nos permita saber cuál es valor del contador x.
¿Qué estructuras de datos me permiten responder una pregunta como esta? Pues, en realidad, depende de qué sea esa x. Si la x es el índice del contador (esto es, «quiero saber el valor del «primer» contador», «quiero modificar el valor del «tercer» contador», etc), un array de números me sirve: el valor del primer contador está en la posición 0, el valor del segundo en la posición 1, etc.
Pero si x es un identificador único para cada contador, lo mismo tengo que pensar en soluciones alternativas. Por ejemplo, un objeto en el que cada clave sea el identificador del contador y su valor es el número con su valor me puede servir:
const counters = {
ae13a: 0,
f18bb: 3,
e889a: 1,
8b1d3: -5,
};
pero ojo, porque también podría usar un array de objetos como el siguiente:
const counters = [
{ id: 'ae13a', value: 0 },
{ id: 'f18bb', value: 3 },
{ id: 'e889a', value: 1 },
{ id: '8b1d3', value: -5 },
];
Si primero defines la interfaz para manipular y acceder a los datos, la implementación que uses luego por detrás es indiferente. De hecho, podrás cambiarla en cualquier momento según te convenga, lo cual es bastante cómodo.
Creando un store basado en Redux con WordPress
El paquete @wordpress/data
de WordPress es un módulo que nos permite definir un almacén en el que almacenar (valga la redundancia) el estado de nuestra aplicación. Para crear un nuevo store usando este paquete, tenemos disponible la función registerStore
, a la que deberemos pasarle la siguiente información:
- Un nombre que identifique nuestro store
- Un objeto
selectors
con las funciones que nos permiten consultar el estado de nuestro store - Otro objeto
actions
con las funciones que nos permiten modificar el estado de nuestro store - Un último objeto
reducer
que se encarga de actualizar el store cuando éste recibe una acción concreta
Como esto suena un poco abstracto, vamos a crear el store en nuestro ejemplo. Abre el ejemplo que empezamos la semana pasada y crea una nueva carpeta store
en src
. Luego, crea un fichero index.js
con el siguiente código:
// Load dependencies
import { registerStore } from '@wordpress/data';
import reducer from './reducer';
import * as actions from './actions';
import * as selectors from './selectors';
registerStore( 'react-example/counters', {
actions,
reducer,
selectors,
} );
en el que le pasamos las cuatros cosas que acabo de comentarte. Puedes darle el nombre que quieras a tu store, pero te recomiendo que sea una combinación del nombre de tu plugin (react-example
) y una palabra que defina lo que almacenas en él (counters
).
Si te fijas, el fichero tiene tres import
de cosas que aún no hemos creado: reducer
, actions
y selectors
. Vamos a crearlos uno a uno y ver qué tienen de especial cada uno de ellos.

Nelio Unlocker
Pásate a WordPress manteniendo tus diseños y contenidos
Mejora hoy mismo el SEO de tu web y acelera su velocidad de carga convirtiendo tus páginas a estándares HTML, CSS y WordPress. No necesitas conocimientos técnicos y solo pagarás por aquello que necesites.
Acciones en un store
Una de las cosas que (en principio) todo store necesita son un conjunto de funciones que nos permitan modificar los datos que tenemos almacenados. En nuestro ejemplo, esto lo vamos a meter en el fichero src/store/actions.js
y tendrá la siguiente pinta:
export function addCounter( counterId ) {
return {
type: 'ADD_COUNTER',
counterId,
};
}
export function removeCounter( counterId ) {
return {
type: 'REMOVE_COUNTER',
counterId,
};
}
export function setCounterValue( counterId, value ) {
return {
type: 'SET_COUNTER_VALUE',
counterId,
value,
};
}
Como puedes ver, definimos (y exportamos) tres funciones para manipular el store:
addCounter
: esta función nos permitirá añadir un nuevo contador en nuestra aplicación. Como parámetro, esperamos recibir el identificador único que tendrá el nuevo contador.removeCounter
: nos permite eliminar un contador de nuestra aplicación. Lógicamente, también recibe como parámetro el identificador del contador a eliminar.setCounterValue
: permite establecer el valor concreto de un contador. En este caso, la signatura de la operación requiere que le pasemos el identificador del contador a actualizar junto al nuevo valor que debe tener.
Lo único raro que tienen estas acciones es el cuerpo de las mismas. Uno esperaría que estas funciones modificaran el estado de nuestra aplicación y, sin embargo, lo único que están haciendo es devolver un objeto. ¿Por qué?
En la arquitectura Redux (de la cual bebe WordPress), las acciones para modificar un store no modifican directamente el store, sino que lanzan (devuelven) un objeto con una «petición de modificación». Estas peticiones siempre siguen el mismo patrón: son un objeto con un atributo type
que identifica la petición de forma única y tantas propiedades adicionales como sean necesarias para pasar los datos asociados a la modificación.
Actualizando el estado de un store usando su reducer
Si las acciones de un store solo representan una petición de actualización, necesitamos alguien o algo que actualice el estado cuando llega una de estas peticiones. Esta es precisamente la tarea del reducer de un store.
Un reducer es una función que toma como parámetros el estado actual de nuestra aplicación junto con una acción para actualizarlo y devuelve como resultado un nuevo estado en el que (si procede) se le ha aplicado la actualización.
En el apartado anterior hemos visto que nuestro store tiene tres acciones, así que nuestro reducer deberá ser capaz de reaccionar a cada una de ellas. Este podría ser el código de nuestro reducer, el cual meteríamos en el fichero reducer.js
:
import { omit } from 'lodash';
export default function reducer( state = {}, action ) {
switch ( action.type ) {
case 'ADD_COUNTER':
return {
...state,
[ action.counterId ]: 0,
};
case 'REMOVE_COUNTER':
return omit( state, action.counterId );
case 'SET_COUNTER_VALUE':
return {
...state,
[ action.counterId ]: action.value,
};
}
return state;
}
Tal y como ya te he avanzado, el reducer recibe el estado anterior del almacén (state
, que, por cierto, por defecto es el objeto vacío {}
) y una acción que pide actualizarlo. El cuerpo del reducer es bastante sencillo:
- Empieza con una instrucción
switch
para discernir el tipo de acción (action.type
) que hemos recibido y actuar en consecuencia:- Si se trata de
ADD_COUNTER
, genera un nuevo objeto que es una copia del anterior al que le añade una nueva clave usando el identificador que viene en la propia acción (action.counterId
). - Si se trata de
REMOVE_COUNTER
, le quita al estadostate
la claveaction.counterId
. - Si es
SET_COUNTER_VALUE
, pone el nuevo valoraction.value
al contador con identificadoraction.counterId
.
- Si se trata de
Lo más importante aquí es darte cuenta de que un reducer debe ser una función pura. Esto quiere decir que toda modificación que hagamos en el estado actual pasa por construir un nuevo estado que incluya los cambios pertinentes. Bajo ningún concepto, pues, debes mutar el estado anterior.
Selectores de un store
Ahora que ya sabes cómo actualizar los datos que tienes en un store, lo único que te queda por aprender es cómo consultarlos. Para ello, debemos definir un fichero selectors.js
con el siguiente contenido:
export function getCounterIds( state ) {
return Object.keys( state );
}
export function getCounterValue( state, counterId ) {
return state[ counterId ];
}
Ninguna sorpresa, ¿verdad? Los selectores de un store tienen siempre, como mínimo, el parámetro state
con toda la información del estado actual del store, junto con cualquier otro parámetro que pueda ser necesario para realizar la consulta que te interese.
En nuestro caso, por ejemplo, hemos creado dos selectores:
getCounterIds
devuelve un array con los identificadores de todos los selectores que tenemos en nuestro store. Como el store es un objeto en el que las claves son los identificadores de los contadores, unObject.keys
nos da la solución que queremos.getCounterValue
nos da el valor exacto del contador especificado. En este caso, el selector tiene dos parámetros: el estadostate
de nuestra aplicación y el identificadorcounterId
del contador que nos interesa. Usando ambos parámetros, podemos ir a buscar el valor que nos interesa usando como clavecounterId
enstate
.
Cómo probar nuestro store
Para probar que el store funciona, abre el fichero src/index.js
y añade un pequeño import
al principio:
// Import dependencies
import { render } from '@wordpress/element';
import './store';
import { Counter } from './components/counter';
...
y luego, lógicamente, transpila el resultado con un npm run build
. A continuación, accede a la página del plugin en tu WordPress, abre la consola JavaScript de tu navegador y prueba lo siguiente:
dispatch = wp.data.dispatch( 'react-example/counters' );
select = wp.data.select( 'react-example/counters' );
dispatch.addCounter( 'a' );
dispatch.addCounter( 'b' );
dispatch.addCounter( 'c' );
dispatch.setCounterValue( 'a', 3 );
select.getCounterIds();
// Array(3) [ "a", "b", "c" ]
select.getCounterValue( 'a' );
// 3
y podrás ver cómo tu store funciona correctamente. De hecho, existe una extensión tanto para Firefox como para Chrome llamada Redux DevTools que te permite consultar el estado de tu store, ver qué acciones va recibiendo y cómo las mismas van modificando el estado del store:

¡Una auténtica pasada!
Próximos pasos
La verdad es que la entrada de hoy me ha salido un poco más extensa de lo que esperaba. Pero espero que la explicación te ayude a entender cómo funcionan los stores de WordPress y cómo puedes usarlos para almacenar los datos de tu aplicación.
La semana que viene avanzaremos con el ejemplo de hoy y conectaremos nuestros componentes React al store para que (a) lo que vemos en nuestra interfaz de usuario sea un reflejo de lo que hay en el store y para que (b) las interacciones del usuario con la UI sirvan para actualizar el store.
Mientras esperas, te voy a dejar con un pequeño ejercicio, cuya solución veremos también la semana que viene. Modifica el reducer para que los datos se almacenen así:
const counters = [
{ id: 'ae13a', value: 0 },
{ id: 'f18bb', value: 3 },
{ id: 'e889a', value: 1 },
{ id: '8b1d3', value: -5 },
];
y actualiza los selectores, el reducer y las acciones para que todo siga funcionando como en el ejemplo que hemos hoy. ¡Suerte!
Imagen destacada de Annie Theby en Unsplash.
Deja una respuesta