Headless WooCommerce & Next.js: Set up Redux Toolkit for State Management

Leo Chan
Geek Culture
Published in
5 min readAug 2, 2021
Photo by sarandy westfall on Unsplash

At some point we have to deal with state management. I think the useState hook is great for most situations but the ever-popular Redux gives you that bit more flexibility across different components. In this article I’ll show you how I use Redux Toolkit to manage state for a cart.

I have learned to like Redux but it was a little tricky to get my head around at first. Redux store, reducers, actions, types, slices, dispatch, selectors… There’s a fair bit of terminology to keep in mind but Redux is particularly useful for larger projects which is primarily why I persevered.

React Context API is another way of sharing data across components without having to pass props down manually at every level.

On Redux’s official website, they tell you that, “Redux Toolkit is our official recommended approach for writing Redux logic”. I figured it’s probably best to follow their recommendation and their Redux Toolkit website contains good documentation and tutorials to follow.

Installing Redux Toolkit

yarn add @reduxjs/toolkit react-reduxnpm install @reduxjs/toolkit react-redux

As of React Redux v7.2.3, the react-redux package has a dependency on @types/react-redux, so the type definitions will be automatically installed with the library. Otherwise, you'll need to manually install them yourself (typically npm install @types/react-redux or yarn add @types/react-redux --dev).

Configure the Store

At its core we need to set up a Redux store so we can save and retrieve data from the store within any functional component. To do this, I like to create a \store folder in the root and keep everything in one place.

You can see we have created a store using configureStore and here we can pass multiple reducers into the store to keep things organised. Think of these reducers as state containers. Each container is essentially an object of key-value pairs storing data in a structure you define. I included a cartReducer which we will come to in a moment.

Create Typed Hooks

Redux has useDispatch and useSelector hooks that help us update the state and retrieve the state, respectively. With TypeScript apps Redux Toolkit suggests creating typed versions of the useDispatch and useSelector hooks to make it easier and more reliable to use throughout your app.

With the RootState type and AppDispatch type exported in the store.ts file we can create a separate hooks.ts file and create the typed versions of useDispatch and useSelector. These can then be imported into any component file that needs to use the hooks.

Create Redux Slices

The way I like to think of slices is that a Redux slice allows us to manage state containers. In the example store.ts above I passed in cart: cartReducer as one of my state containers. For each state container we will want to define what data it stores as its state and also define specific actions that let us change the state (e.g. one action to increase a number counter by 1 or another action to toggle a boolean flag).

Limited version of the cartSlice.ts

The example above gives an idea of the structure for a slice but it still needs more work to define the reducers and export them as actions.

The first thing we’re doing is defining the interface type for our state data — I’m calling it CartState. For our CartState all I want to store is an array of LineItem for now. You can add more data and define it as necessary.

Now that we know the interface has been defined we can move on to define what we want our initial state to be. I want to start with an empty array. You can set whatever default you want as long as it matches the types that you declared in the interface.

Next up, we are creating the slice itself with createSlice. Here we can give it a name and pass through the initial state that we just defined. We then need to create reducers that allow us to manipulate the state. These reducers are then exported as actions with cartSlice.actions.

Finally, we export cartSlice.reducer so we can pass this into store.ts.

Create Case Reducers/Actions in Redux Slice

I must admit the terminology around reducers and actions does confuse me. For the longest time I was used to actions being the functions that allow us to manipulate state. Now with createSlice we’re passing in case reducers as the functions that allow us to manipulate state. Case reducers or actions?

So, case reducer functions defined in the reducer argument in createSlice will have a corresponding action creator and will be included in the slice’s actions field. Whilst this is a cumbersome answer it helps me realise how closely linked the two terms are. For me, it’s more useful to think of these functions as actions as it’s more accurately descriptive.

In my example, I want to create a few actions to manage the cart state and one of them is to add a line item. We can write the function directly in cartSlice but I have abstracted the functions so I could refactor them to a different file if the number of functions grows too large.

You can see addLineItemReducer takes two arguments: state and action. state simply allows the function to access the data currently held in this state container. action is a typed PayloadAction and essentially holds any data that we want to pass into this function. In this example, we will have a LineItem that we pass (as an argument) into this function when we call it.

The first thing I want to do is check if the LineItem we passed in the action payload already exists in our state. I do this using .findIndex which returns -1 if it is not in the array else it gives us the index of the LineItem in our state array. The rest of the logic is to add the LineItem to the state array if we don’t already have it and if we do have it then update the quantity for that LineItem in the state.

Now let’s flesh out the previous example of cartSlice with our addLineItemReducer.

You can see we have added it to createSlice as addLineItem and we export this action function to use in other components.

Use Typed Hooks in Components

We can use our recently created useAppDispatch hook to dispatch the addLineItem action in a functional component of our choice.

Import useAppDispatch and addLineItem. Next, instantiate useAppDispatch:

const dispatch = useAppDispatch()

To dispatch addLineItem and add a specific line item to the cart state we can use something like the following line of code where lineItem is a LineItem object.

dispatch(addLineItem(lineItem))

If you want to access the Redux store and the cart reducer/state then you can import useAppSelector and use it like so.

const cartState = useAppSelector((state) => state.cart)

In our example cartState only holds an array of LineItem and we can access this by cartState.lineItems.

Finishing up my Cart Slice

I wanted to add a few more reducers/actions to my cartSlice and here they are for your information.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Leo Chan
Leo Chan

Written by Leo Chan

Lockdown coder: transformation from non-coder to coder in under 12 months…complete-ish.

Responses (1)

Write a response

Recommended from Medium

Lists

See more recommendations