Small update on Redux usage
Alexander Vechy
Posted on December 14, 2019
So the first Redux version was developed somewhere around 5 years from now and a lot has changed since then.
Not only React improved the way our code works for the end-users but also we, as developers, changed the way we use React. Best practices come and go, leaving us with the code that other, new developers, might not understand the purpose of. Remember that PureComponent
thing? That it was added in the time where everyone was obsessed with React performance before even having any issues with that? Or smart and dumb components?
Why those practices change is another topic, I guess more related to the way software products are developed rather than tied to React or Front End. So here I just wanted to share a few best practices I believe made Redux usage better over the years.
Folder structure
So far I've mostly seen a separation of files based on their purpose:
Then Redux Ducks came into play, making people to separate files and folders by their features or model they work with. This is a great approach suggested by the Redux Style Guide
Notice that files were separated only when the feature became too big to manage in one file. First, actions.ts
were separated from index.ts
file. Then came others, making the index to export createReducer
only, with imported reducer
and initial state. This enables to dispatch specific actions from the specific feature. Why this approach works well? I will agree with Monica Lent that constraints make software easier to maintain. In this case, we constrain code to their features.
But it never works like that in real life! Domain models are interconnected! Actions to one module can cause changes in another! That's real life!
Alright, that's correct. So how about...
Event-driven reducers
Event-driven reducers mean that instead of making action type names to describe what you want to change in the store, you describe what just happened. E.g. instead of ADD_ITEM
you say ITEM_ADDED
and bum your reducer now reacts to the event, that item was added to the basket. Then it means that if you want some other reducer to change the state based on this action, you just add to that another reducer one more switch clause. If you want to know more, check out this talk by Yazan Alaboudi.
And small example from Redux Style Guide
Compare this two:
{ type: "food/orderAdded", payload: {pizza: 1, coke: 1} }
with
{
type: "orders/setPizzasOrdered",
payload: {
amount: getState().orders.pizza + 1,
}
}
{
type: "orders/setCokesOrdered",
payload: {
amount: getState().orders.coke + 1,
}
}
With more complicated examples it gets even more beneficial, not only on the code sources side, but on the cognitive load as well, perceiving your reducers not as "for this action -> update this field", but rather "when this happens -> this updates".
It's all interconnected 🔝
So you probably don't need const ADD_ITEM_TO_THE_BASKET = 'ADD_ITEM_TO_THE_BASKET';
. If your store is strictly separated by domain models or features and not by your UI code, you can separate events that occur in your system by such features. So your events can look like
{ type: 'food/orderAdded', payload: { /* order info */ }
{ type: 'posts/clapped', payload: { times: 11 } }
This way your actions are safe. Logic changes, how we handle events can change as well, but events doesn't. Or at least not that often. You can read about it there.
Second, you can easily react to the event from multiple places in your store. So instead of having:
dispatch({ type: 'ADD_CLAP' });
dispatch({ type: 'SET_COMMENTS_AVAILABLE', payload: true });
you can have multiple reducers reacting to the same event:
dispatch({ type: 'posts/clapped', payload: { /* item */ });
// in your reducers
// posts.js
case 'posts/clapped':
return { ...state, claps: state.claps + 1 };
// comments.js
case 'posts/clapped':
return { ...state, commentsAvailable: true };
And so we're moving our logic to reducers, instead of making calculations in components' code, redux-thunk
s, or redux-sagas
. We know the logic belongs to the reducer, so we move it there. You can read more about it there.
So as you can see, best practices, which enable us to write maintainable Redux store are all interconnected: with one thing you can easier do another, without sacrificing on anything. This post wasn't intended to be "look what I found", or "one more best practice in Redux". These are just simple and short rules you can incorporate (you probably did already, partially, unconsciously, because it's a natural approach) to your Redux store architecture. And there are more of these already described in Redux Style Guide, concluded by developers to make your life easier. Please, take a quick look, maybe you can find something you have question about to ask here!
Be good, smile and tell jokes to each other!
Cover image reference: React Redux Tutorial for Beginners: Simply Explained
Posted on December 14, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.