How to use effect and reducer for simple yet scalable state management system in React application ⚖️
In this post, I would like to share something awesome I have learnt from React experts including core team about effect and reducer for an effective state management system in React. Stay tuned and read on guys 😎
Recently, I have found from the Internet something fun yet fantastic to use for complex state management in React application regardless of the scale. Everything was started by this short tweet from ex-React core team - Dan Abramov. He demonstrated how to combine effect and reducer together to handle a pretty complicated animation mechanism in hover card from bluesky social app. And I noticed David Khourshid also got tagged in the post. I knew there must be something going on with state machine in which David is one of the industry leader in this field. The tweet even stunned some other React expert such as Dominik (react-query's core maintainer). I couldn't wait and eager to check out what's going right away the PR shared by Dan. What really caught my attention at the first glance was this line as he stored an effect inside a state object 🤯 This blew my mind away as I have always seen state as a place to store something that meant to be rendered in the UI, not something to be executed later such as function, and in this case even more special - function returns a function. I was shocked at first, how come I never knew about this before, this time it came from Dan - it must be good 😍
I started digging more and noticed it's not something new. Somehow, it never got much traction in the crazy day of Internet where everything is overflowed by massive amount of content created everyday. The idea was pioneered by another ex-React core team Sophie Alpert, later formularized by David Khourshid to become a library so-called useEffectReducer. I spent sometimes reading throughout those implementation before concluding on this patter - useEffect + useReducer = amazing ✨
I decided to create a sample application to mimic minimal yet complete real-world application with these two criteria in mind:
- synchronous operation should be taken care by reducer
- asynchronous operation should be taken care by effect
My application logic is pretty simple yet resemble how real app would look like. Whenever you make an edition to either message
or weight
, it will update form fields immediately and synchronously including trigger the loading indicator. Yet, it also creates an effect to be debounced and executed later by React's useEffect
hook to asynchronously fetch new data from server to update shipping cost. The beauty of it does not stop there in term of simplicity, try to slow down your network and making update to the form while there is some in-flight request! Not only the form fields are updated right away but also requests got cancelled automatically by useEffect
executing returned cleanup function and the new async request got placed into the network 🤩
These two powerful rules make up a whole data flow in application simple yet effective. Better yet, since useEffect
in React will execute a returned cleanup function if we give it one. By having an effect return a function like so. We are able to be stress-free about things like race-condition and cleanup on route navigation because it always got called at the right time 😉
In my sample repository, I provide three variants of this pattern including:
- App built with Redux Toolkit 🤘
- App built with useSyncExternalStore, useRef and useEffect 🤘
- App built with useReducer and useEffect 🤘
I wanted to demonstrate how flexible this pattern is regardless of your current codebase's implementation as you have have Redux
or without it, useReducer
, or useSyncExternalStore
. The key take away to always make sure synchronous and asynchronous are taken care by the right piece of code 😊
Everything should be made as simple as possible, but not simpler
The concept of effect and reducer used in combination really reminds me of this beautiful quote from Albert Einstein ❤️🔥. Everything can be so well captured by just reducer and effect because there are only two kinds of actions in web application - synchronous and asynchronous
There you have it guys, my experience in learning something new and brilliant from React community. One more crafty thing to have under your belt and ready to rock and roll when ya need it 🤘. I hope you find this post useful and can improve your code quality when it comes to simple yet effect state management. Together, we make the world a better place through quality software 💞
❤️❤️❤️ Be well, Be happy ❤️❤️❤️