React Context was introduced a while back but got traction only after React Hooks were rolled out with React 16.8. While Context does solve the problem it was designed to solve, it is not a go-to solution for global state management, as I will explain in this article.
It is very hard to debug
On the screenshot above, you can see the Redux Devtools extension. It lets you examine every action in detail, offers diff and complete views of the state, and can rewind actions to inspect what went wrong.
This is Redux Logger, which is another debugging tool in case Redux DevTools was not enough or not applicable in your environment. It will log every dispatched action and show you the state before and after applying it. Redux Logger cannot rewind, though. But it still can do more than any such tool for context.
This is React Context DevTool. It lets you view the context state. And that is it. There is a diff view as well, but it is not clear how that works with no actions dispatched. I will add that this is a project at an early stage with only 2 contributors working on it. So, while the tool is impressive and I am grateful to its developers, it is not at a very useful stage yet.
You could also use the React DevTools, but it does not work in every environment and still lacks most features of Redux DevTools. There is also a way to tie Context with Redux DevTools, but given the complexity of this solution, I would just use Redux in the first place.
It cannot be extended
One of the most powerful features of Redux is enhancers. One of the most used enhancers is middleware, which lets you override the dispatch
function. You may have used such middlewares in your projects, like redux-thunk, redux-logger, redux-saga, etc. You can also write custom middlewares to embed logic directly in the store, reducing boilerplate and repetition.
React Context does not offer such interfaces. Your choices are to either embed this logic elsewhere (components/helpers) or to use the useReducer
hook and manually wrap the dispatch
function. But even then it would not be able to implement half of what Redux enhancers can.
It does not separate logic from presentation
This is an extension of the previous argument. Since there is no way to embed custom behavior into Context, you have to do it in your components. Now you begin to mix logic and presentation in the same entities. Sure, you can separate your components into container components and dumb components, but has that ever really worked for everyone?
With Redux and middlewares such as Thunk or Saga, you can describe your business logic inside action creators and/or custom middlewares. This lets you use only dumb components and have a concrete separation between business logic and presentation.
It is slow
Yes, that is it. React Context is slow, period. This happens because there is no selector mechanism, like in Redux. That means when any value in the context changes, every component that consumes this context will rerender, regardless if it actually uses it. It can be partly mitigated using React.memo.
Redux, on the other hand, has no such problem. Both with HOS and Hooks, you define exactly what data do you need and your component will rerender only when that data changes. That means you do not need unnecessary React.memo
calls.
It is not shorter/easier
One of the arguments for Context you often hear is that it is shorter/easier than Redux. Well, let us check. I wrote out 4 examples for you: using Context, using Context + useReducer, Redux, and Redux + Redux Toolkit:
The components and the state logic are supposed to be in separate files but I omitted it for the sake of simplicity. You can see that the examples with Redux are at least not longer than the Context ones. Moreover, you only need one Redux state in your application (and use slices to make sub-states) but you most likely will need more than one context, which results in double the boilerplate.
Closing notes
Of course, there are always use cases for React Context. It is perfect for storing theme options, locale settings, and other properties that do not change often. But if you start with Context and your app ever grows and becomes complex, you will have to spend time rewriting Context into Redux because of readability/performance issues.
Thank you for reading, I hope you enjoyed this article. Let me know in the comments what do you think about the Context and Redux dichotomy!
Resources
- The original issue in Redux Github explaining why Redux is not using Context under the hood
- Context docs