When building complex WordPress block editor (Gutenberg) applications, managing state efficiently becomes crucial. This is where @wordpress/data
comes into play. It allows you to manage and share global state across different blocks and components in your WordPress application.
If you’re new to managing global state or using @wordpress/data
, don’t worry! This blog post will walk you through the basics of reducers, actions, and selectors, explaining how to use them to manage state in WordPress.
@wordpress/data
provides a way to store, update, and access data globally, allowing different components or blocks to share and interact with the same data.
Key Concepts in @wordpress/data
To understand how to use @wordpress/data
, we need to understand three main concepts: reducers, actions, and selectors. These form the core of how state is managed and updated.
Actions:
An action is like an instruction or command that tells the reducer what to do. It’s simply an object that has two parts:
- A type that indicates what kind of change is happening (e.g., add, remove, update).
- A payload that contains the data needed for that change (e.g., which item to add).
Here’s an example of how actions might look in our cart example:
const actions = {
addToCart(item) {
return {
type: 'ADD_TO_CART', // Action type
item // Payload: the item to add to the cart
};
},
removeFromCart(itemId) {
return {
type: 'REMOVE_FROM_CART', // Action type
itemId // Payload: the ID of the item to remove
};
}
};
In short: Actions tell the reducer what needs to change in the state.
Reducers:
A reducer is like a manager of your state. Whenever something changes in your application (e.g., a user adds a block or disables a feature), the reducer listens for that change and updates the state accordingly.
What does a reducer do? It takes the current state and an action, then returns a new state based on the action.
Here’s an example of a simple reducer that manages a shopping cart:
const reducer = (state = { cart: [] }, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return {
...state, // Keep the existing state
cart: [...state.cart, action.item] // Add the new item to the cart
};
case 'REMOVE_FROM_CART':
return {
...state,
cart: state.cart.filter((item) => item.id !== action.itemId) // Remove the item from the cart
};
default:
return state; // Return the unchanged state for unknown actions
}
};
In short: The reducer defines how the state changes when specific actions are dispatched.
Selectors:
A selector is a function that retrieves or selects specific data from the state. When your components need to access data (like displaying the items in the cart), they use a selector to fetch that data from the store.
For example, a selector to get all the cart items might look like this:
const selectors = {
getCartItems(state) {
return state.cart; // Return the cart items from the state
}
};
In a component, you would use this selector to access the cart data like this:
const cartItems = useSelect((select) => select('my-store').getCartItems());
In short: A selector is a helper function that lets you access specific data from the state.
Step by Step Guide to Implement Global State in Gutenberg with @wordpress/data
Now that we’ve covered the basics, let’s walk through how you can implement these concepts in a Gutenberg block or component. We'll set up a simple store with @wordpress/data
, manage some state, and use actions and selectors to interact with that state.
Step 1: Define Your Initial State
First, we need to define the initial state of our store. This is the default data that our application starts with:
const DEFAULT_STATE = {
cart: []
};
Step 2: Create Actions
Next, we define the actions that we will use to update the state. In our case, we’ll define two actions: one to add an item to the cart and another to remove an item.
const actions = {
addToCart(item) {
return {
type: 'ADD_TO_CART',
item
};
},
removeFromCart(itemId) {
return {
type: 'REMOVE_FROM_CART',
itemId
};
}
};
Step 3: Create the Reducer
The reducer listens for dispatched actions and updates the state accordingly. Here's our reducer, which updates the cart when actions are dispatched:
const reducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return {
...state,
cart: [...state.cart, action.item]
};
case 'REMOVE_FROM_CART':
return {
...state,
cart: state.cart.filter((item) => item.id !== action.itemId)
};
default:
return state;
}
};
Step 4: Create Selectors
Selectors help retrieve specific data from the state. For example, if we want to get all items in the cart, we would create a selector like this:
const selectors = {
getCartItems(state) {
return state.cart;
}
};
Step 5: Create and Register the Store
Finally, we’ll create and register the store with the @wordpress/data
package. This will make the store globally accessible across your WordPress site.
import { createReduxStore, register } from '@wordpress/data';
const store = createReduxStore('my-cart-store', {
reducer,
actions,
selectors,
});
register(store);
Step 6: Using the Store in Components
Once the store is registered, you can use it in your Gutenberg blocks or components. For example, to add an item to the cart:
import { useDispatch } from '@wordpress/data';
const { addToCart } = useDispatch('my-cart-store');
addToCart({ id: 1, name: 'Sample Item' });
To fetch the items in the cart:
import { useSelect } from '@wordpress/data';
const cartItems = useSelect((select) => select('my-cart-store').getCartItems());
Conclusion
By understanding the roles of reducers, actions, and selectors, you can easily manage global state in your WordPress Gutenberg projects using @wordpress/data
. This structured approach allows you to manage data more efficiently, making your blocks and components more powerful and interactive.
With @wordpress/data
, you have a reliable and scalable solution for handling state across your entire WordPress application. Give it a try in your next Gutenberg project!
Top comments (0)