Redux nomenclature from old to new
I’ve been working on React Apps that have been using Redux for quite a few years now. When I first started using Redux there was no Redux Toolkit (or at least we weren’t using it) and the whole concept at first took me quite a while to get my head around.
Developers were complaining about having to declare constants, writing indirect code, touching multiple files to implement features, and so on.
The tooling around Redux has evolved quite a bit over the years. It seems this was done to try and improve the developer experience and make it easier to implement and also to soften the learning curve when first starting out with Redux. Developers were complaining about having to declare constants, writing indirect code, touching multiple files to implement features, and so on.
I have to say that from personal experience I do feel the team behind Redux Toolkit succeeded in making the developer experience more pleasant and it’s now relatively straight forward to implement Redux into your application. The tools keep evolving (Redux Toolkit version 2.0.0 coming out recently), the documentation has gotten really good and usage remains high.
However, due to this evolution and the tooling around Redux I have never been completely comfortable with the terminology in Redux. For me, terminology is very important. If you can’t articulate well where an issue is or how something works to other developers then you are likely to run into problems.
Redux is deliberately unopinionated and lets you decide how you want to implement everything. This led to lots of confusion though in the developer community (people actually like being told how to use something!). React toolkit helped solve many of these problems but it introduced terms that obfuscated some of the original meanings in Redux for me. Don’t get me wrong, it certainly make things easier and more consistent across code bases but I have since been a little unsure about the naming of things.
If you can’t articulate well where an issue is or how something works to other developers then you are likely to run into problems.
This is the point of this article, to dissect the terminology around Redux and Redux Toolkit and try and clear up as much as possible the different meanings of words found when using Redux. (I’m basically writing this article for myself as a reference but hopefully it might be helpful for other people too.)
Basics
Lets start with the basic definitions that were introduced back in the infancy of Redux.
Actions
An action is a plain JS object that represent an intention to change the state of an application. Something like:
{
type: 'ADD_TODO',
payload: {
text: 'Wash the car'
}
}
The action has a type
property and this action has a payload
property (this is optional). These actions are dispatched to the Redux store using the store’s dispatch function.
Action Creators
An action creator is a function that returns an action. Something like:
function addTodo(text) {
return {
type : "ADD_TODO",
payload: { text },
}
}
There are (were) some good reasons for creating action creators. See this insightful blog post for a more in depth discussion about these reasons. The main reasons seems to be it’s just better practice to have a function that creates an action once and then reuse that function throughout your application. DRY principles.
State
In Redux the state is stored as a plain JS object and it’s managed by the Redux store. The structure of this state really depends on your application and UI and it’s totally up to the developers implementing Redux in their application on how it should look.
A very simple example of the state of a Todo app could look like this (this is just a snapshot - in a real app the state is continually updated).
{
todos: [
{
id: 1,
text: 'Wash the car',
completed: false
},
{
id: 2,
text: 'Feed the monkey',
completed: false
}
]
}
Dispatch
This is very simply sending actions to the Redux store to update the state. When this action is dispatched it goes through a reducer in the store which takes the current state and the given action and then updates the state based on the action type and it’s payload.
An example could be a button click on the UI causes a dispatch to send an action to the store which then updates state which in turn updates a piece of UI.
Reducers
Ah reducers. This word has always intimidated me. I’ve constantly struggled to get a good grasp of this word in the context of Redux. I think the main thing I struggled with was understanding what their actual role was in the whole Redux system. Once I finally understood that everything seemed much clearer.
A reducer is a function that determines the state of the application by responding to actions. If we look at the following code we can see this reducer has a switch statement which depends on the action.type
. If the action.type
is ADD_TODO
it will update (immutably) and return the state one way, if its TOGGLE_TODO
it will update the state (immutably) differently and return the state.
function todosReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false,
},
];
case 'TOGGLE_TODO':
return state.map(todo =>
(todo.id === action.id) ? {...todo, completed: !todo.completed} : todo
);
default:
return state;
}
}
Store
The store in a Redux application is the central repository for the state of the application. The store is like the brain of redux and it’s the place where the state
, actions
and reducers
are all brought together. The main responsibilities of the store are:
- Hold the application state
- Handles the reducer logic
- Registers listeners via
subscribe(listener)
so parts of your application can update when certain state updates are made - Handles the dispatch of actions
- Allows access to the state
Redux Toolkit
As stated on the Redux Toolkit documentation — the goal of Redux Toolkit is to help simplify common Redux use cases. Sounds like a pretty healthy goal to strive for. It provides a bunch of helpful functions that enable you to implement Redux into your project easily, organise your Redux code and generate much of the boiler plate needed without you and your team having to make loads of decisions.
Slices
Slices are ways of organising your Redux code into parts which are then defined by the reducers. Maybe in your Todo app you introduce the concept of users
. Rather than having everything in one big reducer which could become unweildly as your app grows, slices allow you to split these reducers up into logical parts and then use the combineReducers
function from Redux to bring them altogether.
Redux Toolkit provides a helpful createSlice
function that will auto-generate the action types and action creators for you, based on the names of the reducer functions you provide. (You can see why this library became popular!)
Asynchronous Logic
Now we get into the territory of asynchronous logic which can include data fetching. Redux by itself is completely unaware of any async logic. Everything it does is synchronous whether it be dispatching actions, updating the state or notifying the UI that something has been updated.
So this is where Redux Middleware comes in and with that a bunch of new terminology… Hold on to your hats.
Redux Middleware
Essentially this allows code to be injected into the dispatch process between the dispatching of an action and the moment it reaches the reducer. It lets you do cool stuff like make some call to some api and then update the state based on the response. Quite a common scenario in applications.
Redux Thunk
‘Thunk’ in my opinion is such an ugly word but if you want to work with Redux you will inevitably hear it quite a bit. A ‘thunk’ in programming means ‘a piece of code that does some delayed work’. For Redux specifically, ‘thunks’ are a pattern of writing functions with logic inside that can interact with a Redux store’s dispatch
and getState
methods.
Redux-thunk is a piece of middleware that allows you to use thunks with Redux. A classic example being fetching data from an api which is an asynchrounous operation in redux. You dispatch a function (thunk) instead of the usual action, you wait for that async operation to resolve and then upon resolution you dispatch a regular action.
Conclusion
I have realised over the years that when learning a new library half the battle is getting your head around the main terms used in association with that library. It can be very intimidating when using something new especially when it has many unfamiliar terms. Redux is a good example of this. Terms like — reducer, dispatch, store, slices, thunks… Getting a good mental model of what these terms mean and how they come together is imperative for getting a good understanding of Redux.
About me
I am a Frontend Developer working mainly with React and Typescript in my day to day professional life. I love exploring new tools and libraries and love the Javascript Ecosystem.
I like to write blog posts that share exciting new tools I’ve discovered, how-to articles and also the occasional opinion piece.
I live in Prague in the Czech Republic with my family.
Check out the original blog post on my blog.