You might need Redux DevTools 🔱

In this post, I would like to write about my experience created a tool that improving developer experience in working with Redux-like state management architecture. Stay tuned and read on guys 🫠

Redux was created back in 2015 by Dan Abramov and Andrew Clark. It was such an amazing library back then and took over the world by nature. It was the most popular implementation of Flux architecture and highly inspirational source for other implementations across UI frameworks like Vuex for Vue.js, NgRx for Angular, and many others. Modern front-end architecture has evolved and so does the way we work with Redux. I personally believe Redux is still one of the best tools for managing state in React applications at scale given the right context and use cases. It has matured for almost a decade and has been proven to be a reliable solution for many large-scale applications. However, it's not perfect and there are still some pain points that we need to address. If your applications have a lot of moving parts in client-side only. Redux is still one of the best choices for such scenarios. If your applications are really server-oriented data driven, you might want to break up with global state.

Since the birth of React's hooks and Context API, I have seen people are moving away from Redux and opt-in lighter weight native solution like useState, useReducer and useContext. I have seen those in action and it's totally fine until it is not. React-redux is actually using context under the hood. I hope you are not re-creating your own Redux without knowing it because it's not a good idea. Please make sure you have a good understanding of your foundational architecture when it comes to state management in React since it will affect your teams' productivity and the overall performance of your applications.

This post is for those of us who are working with Redux-like state management architecture and want to improve the developer experience with Redux DevTools. There are several pre-conditions that your state management architecture needs to meet in order to benefit from Redux DevTools:

  1. Your state is serializable through JSON.stringify and JSON.parse APIs
  2. You can expose a store object like Redux does with getState, setState and dispatch
export interface Store {
  getState: () => State
  setState: (state: State) => void
  dispatch: (action: Action) => void

I am going to use useSyncExternalStore API with a simple store as an example to demonstrate how to integrate Redux DevTools with a state management system. You can use whichever state management you like as long as it meets the pre-conditions above.

And here comes the fun part. Since Redux DevTools expose an object called __REDUX_DEVTOOLS_EXTENSION__ under global window object, we can use it to subscribe, send the state and action to the DevTools. As a result, we can see the state and action in the DevTools UI. Additionally, with the proper subscribers, we can also send the state back to the our store to update the UI. This is a very powerful feature that can help us to debug and understand the state changes in our applications. And with such simple yet powerful technique, we can integrate Redux DevTools with our state management and enjoy all the benefits that Redux DevTools has to offer 🤯

And here is more if you are setting your application with modern tools that support Hot Module Replacement(HMR) and React's Fast Refresh. At any point in time during your time-travel development with Redux DevTools. You can always log things and put a debugger anywhere in your components that are currently in use like this. Because of how HMR works in harmony with Fast Refresh, whenever your source code changes, the corresponding modules will be updated thanks to HMR and React's Fast Refresh will re-render application accordingly while preserving the current state of your application.

Last but never be the least is my favorite part - stack trace. I invented this idea to figure out which line of code is responsible for the state change. Most of the times in the past, I captured an action in Redux DevTools and then tried to hunt down where it got called. As applications scale, it became such a tedious task and time consuming. I thought to myself, why not just attach a stack trace to the state before sending it to the DevTools. And that's what I did. I used Error.stack to capture the stack trace and attach it to the state before sending it to the DevTools. And it worked like a charm. As you do time travel through your applications, you can see the stack trace in the console and know exactly where the state change is coming from. This is such a powerful feature that I believe every state management architecture should have. It helps you to understand the state changes in your applications and make your life easier.

Finally, it is up to you to decide whether you need Redux DevTools or not in production. I have seen companies done both and it's totally fine. In this case, if you want to exclude Redux DevTools in production, you can use this simple feature supported out of the box by many bundlers so-called tree shaking like below in your devtools setup file:

export const setupDevtools = isDEV ? setupDevtoolsImplementation : undefined

Your final bundle will not include setupDevtools in production. Here is a gist of it 😉

Please feel free to play around with the code here and the demo is already available - Please make sure you have Redux DevTools extenstion installed upfront 😊

There you have it guys, I hope you find this post useful and enjoy the benefits of Redux DevTools in your applications. If you have any questions or feedback, feel free to reach out to me on Twitter. I am always happy to help and learn from you guys. Together, we make the world a better place through quality software 💞

❤️❤️❤️ Be well, Be happy ❤️❤️❤️