![Cover image for React doesn't need state management tool, I said](https://media.dev.to/cdn-cgi/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjawcwoye9j7126xcvjn4.png)
From time to time someone still tells me that is using REDUX or similar tool in their project. I usually respond, that I wouldn't use it as now wit...
For further actions, you may consider blocking this person and/or reporting abuse
My whole reasoning for not using Redux is because Redux is overly complex for what it needs to be. While your solution works well for people that like Redux, you've essentially given no benefit to the developer for picking Context over something like Redux Toolkit, because it does a lot of these things behind the scenes for you already. Instead of setting up a specific way to use Context, you could just load in Redux Toolkit and be done with it - you mentioned this is less boilerplate but I'm not sure I see that?
Maybe it's a bit confusing, that I'm using
dispatch
function. But I only named it this way as a convention, this differs completely from REDUX, I don't use anything like reducers, action creators, store etc. - that's what I mean by reducing boiler plate. I took dispatch as a way to send message to the Provider to make some mutation to the state, thats all.But you literally just used implemented the Redux reducer pattern (
useReducer hook, my mistake) which accepts a reducer function, and returns a dispatcher that accepts actions that will be created by action creators as the project scales. You didn't reduce boilerplate, you just re-created the entire Redux logic without the store setup.Ummm... you just described how Redux works 😅 except you are now forced to write action creators manually to avoid typo issues, when Redux Toolkit could be generating all the action creators for the reducer for you, see:
I don't mean to be disrespectful, but I think you don't understand the article. I haven't used
useReducer
anywhere and I didn't even duplicate it's logic (if that's what you mean). Also I didn't recreated whole redux logic (that would be quite a challenge :D). I don't think that writing action creators is necessary - if you use typescript it should prevent typos, I've suggested that in the article. All in all, I'm glad that you are interested into this topic, but it feels like you are arguing against things that I haven't wrote in the article.My apologies, you are absolutely right! I missunderstood you reducer implementation using
useRef
withuseReducer
given it looked so much like one. However, you did recreate the reducer and action creator part of it, which were still my original points. See your usage example where you literally wrote something extremely similar to a Redux Reducer and action dispatching:And there's your problem. You are making the assumption everyone uses typescript now. And it's yet another reason why people build action creators... because they just work at ensuring consistent actions types, independent of if you're using typescript or not.
You opened your article with "React doesn't need state management tool {...} From time to time someone still tells me that is using REDUX or similar tool in their project. I usually respond, that I wouldn't use it as now with hooks and context API you don't need it.". That's a pretty big and powerful statement you opened your article with which implies you have an objectively better state management solution than redux in all scenarios. And not just that, but you only listed advantages to your solution, with zero disadvantages.
Just as a heads up, but if you want to make bold claims like that, you need to be prepared to back them up 🙂 And not be surprised when people start pointing out the flaws in your proposed alternative. If you had worded your article's title to "Building your own redux-like state management solution", you can be sure you wouldn't have gotten half as constructive feedback as this 😉 Since at no point would you claim it's a better solution than Redux, just a different one that might be useful for some people. Still, I don't blame you. Hyperbole-like, clickbait-y titles are usually what drives clicks 🤷♂️ Can't hate the player, hate the game.
Yep, I opened the article with a bit of provocation, that's true. I also made sure, it's clear that it's only my personal opinion. But I truly think that this way of managing the state can remove the need of REDUX. I don't say, that it's just this little piece of code that I wrote, but basically instead of using REDUX for everything (state, API calls, forms ...), we use hook libraries like react-query, formik etc. I'm sure people are doing this all the time. However sometimes there is a need for a bit of complex state management and that is what we use this solution for - it enables you to combine multiple hook libraries and create a piece of state, that is:
Also, I'm not really using reducer as is defined by REDUX/FLUX. I think real reducer should get the action and return new state, that's not what I'm doing. I'm only recieving actions, but modifying the state through callbacks like
setState
. It's a subtle difference I know, but this allows you to use it with react-query and not to keep the whole state on one place as "proper" reducer would.Totally fair and agreed. Sorry if my original comment was a little direct.
Btw, but just to end with 0 chance of doubt, did you try Redux Toolkit before making this or writing the article? 🤔 Curious because of this statement:
Reducers made with Redux Toolkit don't return new state either, they only receive an action and mutate state (thanks to an immer compat layer), exactly like you described 😅 See: redux-toolkit.js.org/api/createSlice
If you weren't aware of that, I wonder if this could explain the confusion from all sides 😅 And if so, I hope this explains why we all feel like you just wrote a 1:1 "lite" version of Redux + Redux Toolkit.
Don't get me wrong I think there are still use cases for REDUX and if I ever find myself in a position that I will need that, I'll definitely try redux toolkit or some similar tool.
However I still don't agree that my solution is the same as redux toolkit. I see redux toolkit as a "syntax sugar" for redux to reduce boiler plate, that's fine. However, my main complain is that REDUX brings unnecessary complexity for majority of projects, you can cover up the complexity with tools like redux toolkit. But from my experience it's better to avoid the complexity from the beginning.
I don't think I've re-built redux toolkit, but if so, that would be actually quite bad sign for redux - think about it ... You take quite a complex library, which you need additional toolkit to be usable and you end up having same solution as writing 30 lines of code.
.
.
.
Last part is exaggerated of course :)
You don't need the toolkit for it to be usable. The toolkit is an opinionated way of setting up and using redux but it's just as easy to use redux without it. The power of the toolkit is that you'll be familiar with all other implementation of the toolkit whereas Redux itself gives you a lot of freedom to set it up however you like.
A good example is the addition of
Immer
in the toolkit. It's absolutely not necessary for Redux but you could implement it yourself without the toolkit if you wanted to.This article would be fantastic.... if it had released 2 years ago before Redux Toolkit came out, which sorted out the boilerplate issue for the most part.
I'd hesitate to call this a more scalable solution than something like Recoil or Redux Toolkit since you still need to have a deep understanding of how context affects rendering and performance. And it's why context will always be better suited for sharing primarily static data that won't change, as opposed to a state management tool. Not to mention that it doesn't account for any of the more advanced state management features like middleware and async operations.
This seems great for prototyping small to medium size projects, but not much else. Still, it's definitely neat and worthwhile mentioning. Too many people feel like they need to use a massive state management library like Recoil or Redux for their hobby project when something leaner could be enough.
I wouldn't say that REDUX scales perfectly with big projects. Having one huge state is not always ideal, especially in cases when you have multi page app, then you have parts of your state, which are completely useless for some pages, but they can still ask data from there, because the state is global. I feel, that having limited context provided only to e.g. one page, scales much better as other pages can't access it even if they want to.
Why would state "ask for data" if the pages are not being used? This sounds less like a Redux problem, and more like an architectural problem caused by bad developer planning. Redux state not-in-use doesn't affect memory or performance in any measurable way. And if it does, it's because you probably are doing some kind needless logic somewhere which could also be caused using the limited context pattern.
This isn't a benefit exclusive to limited context. You can achieve the same results by having individual reducers for each page and not subscribing to global changes from other pages. If you want to have state that can't be accessed by other pages... maybe just don't access it from other pages? You make it sound like because it's global, you are forced to use it everywhere and that's just not true.
What confuses me the most, is your article basically ends with you building a lighter, but more limited version of Redux by combining Context + useReducer + use-context-selector. You basically re-invented the modern wheel, except it's lighter, but harder to repair and can only drive on certain roads (and not very fast). Which is great and impressive as a learning exercise, don't get me wrong. But it's missing important key functionality from global state management systems such as middleware, async operation handling, among other things. And calling it a "redux replacement" is a bit of an overstatement.
Other than that, it's a good first article on Dev.to, congrats!
Sure, it really depends on the project though. I understand that in long lasting projects it is really important to have some strong foundation, where REDUX can give you that, it's a tested approach and you know that it won't kick you.
In our case we wanted to be efficient and reduce unnecessary boilerplate, while keeping the performance and the fact that you can build it yourself pretty easily was quite amazing for me :)
That's fair! I agree that Tolgee seems like a rather simple app where perhaps Redux would have been overkill. Kudos to you for taking the initiative and building your own state management solution, I agree it was probably a fun learning experience.
My feedback was merely directed at that this wasn't a viable solution for Redux for anything larger than small projects 😅 Thanks for understanding. Have a good day!
I don't know what is the size apps you usually build, but I wouldn't say that Tolgee is simple app. Actually we decided to stop using Redux, because it grew too much. ⚰️
redux toolkit is not clean or can claim it uses less boilerplate than, recoil, valtio or similar state managers.
I'm more in the hookstate, recoil camp. I just don't like reducers.
I guess it's a lot about personal taste. My goal was to sum up, how to use context in efficient way - but keep it as simple as possible. I basically understand
dispatch
as a way how to send message to the provider to modify the state. And I like that this way you can pass one function, which can handle all the events, that's how I think about it.But this would mean you would now need to create action creators to ensure it scales properly... which is more boilerplate. Boilerplate you wouldn't need to write with Redux Toolkit slices 🤷♂️ To each their own I guess.
What motivates me further to use Redux is that Redux Toolkit (RTK) provides new feature called RTK Query, so that we can define endpoints more directly and intuitively using createApi (redux-toolkit.js.org/rtk-query/api...), instead of previous ways using createSlice, createAsyncThunk, etc.
I would not use RTK, try Valtio + react query
Yes, but if you think more about it sending a message like
dispatch({type:"add_todo", payload})
you can get the same reducer like behavior with a normal dedicated function like
function addTodo(payload) {
Set...
Set...
do something else...
}
So the more you think about it you will see the complexity is bringing you no benefits.
You can easily do this through context too. I would say, that it's just matter of taste. I'd say I chose dispatch, because I see the symbolic of "dispatching event" for the provider, which is the way I like thinking about it. But I don't really have a strong opinion on this ...
Recoil ftw
Check out recoil on bundle phobia, i wouldn't use it with how much it weights, unless your website shouldn't appear on Google.
Bundle size is important, and there is a reason why recoil is in experimental state.
If they could make it weigh 2kb I might consider it.
Recoil is heavy, but it also saves you from writing a lot of boilerplate code.
However, there are also lightweight (tree-shakable) alternatives
with a similar approach.
I haven't used recoil yet. But from documentation it feels like it's a bit more complicated. I tried to keep it simple and similar to React.Context API. But I guess it has very similar use case ...
Imo using it feels way simpler than context. Different api than context, but very very little setup involved
Instead, I prefer Redux toolkit, in any case, we need a kind of complex boilerplate, but with Redux toolkit, I think, that headache was solved. Until Redux toolkit, in small to medium projects, Context API was great for me and it is a part of React, which makes Redux unnecessary but for large projects, I believe currently, Redux toolkit can be accepted as the best, I cannot say it is the best because there can be always a better way.
All of those articles pretend to copy the same names and patterns as redux, so why don’t you go to redux toolkit instead? Contexts solve problems on small projects, but since the complexity is constantly expanding, redux is way simpler to make it more simpler do debug and increment functionalities.
The problem is that I really like using hook libraries like
react-query
and those are not designed to cooperate with REDUX. I think that's also a reason why many people just want to use context, because you keep whole power of hooks in there and you can just pass data to all children.You should check out rtk query. It's part of redux toolkit now and is fantastic. I use the rtk query codegen to automatically create the entire interface with my backend from the swagger. It builds all the redux integration as well as properly typed hooks to use in components.
Yeah... RTK Query seems to make our task in defining endpoints for data fetching easier or more directly/intuitively. Instead of using createSlice and createAsyncThunk for that purpose, we can just use createApi and fetchBaseQuery.
We implemented this pattern across many Next.js apps without issue but our team decided that we wanted to adopt a state tool. Our choices were between Recoil, Jotai, and Zustand. We ended up going with Zustand and everyone seems to love it. However, I’d really like to ramp up with Recoil.
Nice article!
Setting a bunch of hooks and effects in React components really shows what a big mess React is, compared to more elegant solutions with Angular and Vue.
Typed Observable Reactive stores is where it's at and the APIs for using those are much more composable and beautiful. I can't believe React has become such a mess, but I guess that's why it's the oldest of the big frameworks.
I'm crying in
zustand
. It's much simpler to setup & doesn't impact the bundle size much. Now everyone (REDUX FANBOIS) will say redux-toolkit makes setting up redux easier but that's what redux itself should do. Why would I add another library over already installed redux & react-redux summing up my total bundle size with another 40KB?So I now agreed on a combo of ZUSTAND & React-Query which is highly recommended for others too (IMO)
Nice article, I did not know that separating contexts and their childrens help like that
I think React offers
useReducer
which guarantees thedispatch
function to be stable reactjs.org/docs/hooks-reference.h...Yes, but it's kinda different pattern. I like that here you can combine your custom state with hooks and their state ... we use react-query and this way, you can do things like this:
Agree, that's a point and I think it really depends on the need and state architecture of an app. Reducer my in opinion is usually helpful when you have some actions mutating different interconnected states per dispatch.
I used react-query in my project too and I like to wrap
useQuery
/useMutation
under a custom hook that specialize for a business domain.For example:
The problem with React Context is that (due to the nature of React itself) it behaves like a state management tool, while in reality it is a tool for dependency injection. This confusion often leads to the idea to use React Context as a state manager, which it was never intended for.
If you take a closer look at any decent state management solution, you will quickly notice that these tools never pass "naked" state (values that change frequently) directly via context, they instead wrap the state with "something" and then pass this "something" through the context. This "something" is generally called "an observable store".
Different tools use different names ("store" - redux, effector, zustand, etc.; "client (cache)" - apollo-client; "query client (cache)" - react-query), but the end result is the same - "naked" state is wrapped and the referential equality is preserved even when the state changes.
So, instead of using cryptic libraries like
use-context-selector
, it might be a better idea to take advantage of the "observable store" pattern. If you don't feel like using an existing solution, you can easily implement everything yourself, it's like ~30 LOC with all the infrastructure.I also use context API and I'm amazed when people are not aware of what it can do and I'm relatively new to the react world. I haven't tried Redux yet and so far I haven't come around to needing it ....YET
People are aware of what it can do, but it's not used because it's well known React Context was not made for sharing frequently changing state. Because React Context triggers re-renders by default (unless you add custom logic to prevent it, like the author), it's frequently a common cause for performance issues.
It's why React Context's main use is to share static, non-changing data.
That's what I said, the possibility of adding your custom logic using the context API is what I'm talking about. What else would I be referring to and my statement wasn't a blanket statement, the key word being "WHEN" because I have encountered people that don't even have an idea about this aspect. Anyways, I am aware of the benefits of using other libraries. But for smaller projects , this will suffice. There's no need to further bloat it up. It comes down to the requirements of the project.
Ah, I see what you mean. The way you worded it made it sound like you thought people didn't know it could be used for state management (given that's what the article you commented on is entirely about). I was simply replying that it's pretty well known it can be used, just that people prefer not to use or write articles about it since the performance considerations make it somewhat of a foot-gun (a gun prone to miss-fires that you shoot yourself in the foot with).
Also worth mentioning, but the performance considerations could also present themselves even in smaller projects, if you have enough expensive-to-render components depending on the context. Meaning even for smaller projects, Context still might not be the best option.
I agree with you that it comes down to the requirements of the project though.
I'd say, that not if you are not using any library is easier to make things messy or slow. It's good to be aware how exactly context work, think about where to use it and how split the state - however if you do that it's quite rewarding.
This question is not related to Redux vs Context, but would be glad if someone takes the time to answer.
It seems to be a "me" issue, but when and how to decide if I even need a global state management tool at all? I'm currently working on a pretty big project, that will progress in scale as time goes by, but so far, I haven't really felt the need of a global state management..?
We're using react-query for API-related data, and I'm using Context API for simple use cases such as having the sidebar menu of the dashboard opening/closing when clicked from an outside component which triggers update to the whole dashboard, and also using the Context API for i18n language changes.
Besides that, I haven't really felt the need for anything else. Or am I looking incorrectly at the things, and missing something? Haven't really thought of a scenario so far where I'll need something like Redux at all. What are some examples of such scenarios?
Redux may be handy when you want collaborative UI (app like google docs), and want to update actions live for all users because of that event sourcing pattern that Redux use. You just need to pass that actions you're using over websocket and apply to the store.
Because Redux have strong convention it may be plus whey you switch between projects or your project have high devs rotation. Evebody are familiar with this solution so it's easier to work with it. React context can be used diferently depending what developers do.
Also, Redux is easy to debug with dedicated devtools. You can see what happened as the new condition is calculated and you can "rewind time". So during development, developement team with QA team, QA can actually send you serialzied store (or just post it for example in Jira ticket as part of issue/bug) and you can reproduce exact same state of the app. It can be really handy.
In React Contexts, we have standard react devtools and we can only view components.
React Contexts should be modular, but devs often are afraid of having too many Contexts, so they stuff extra responsibilities into existing Contexts, which make them grow very much.
And if we already have a lot of them, there may be chaos in the organization of data flow, and in particular what from whom it depends, because there is no one source of truth.
And finally chaos in tests, because different components can depend on different contexts and it is not visible from the outside.
All of that solutions are just tools and you need pick the best, that fits you need.
I understand Redux has quite a bit of boiler plate but honestly, I hate Hooks so much and wish it never came out. Too many devs are writing sloppy code with them and large projects are getting extremely hard to manage and debug simply because there is no standardization of how to structure custom hooks. Never using Hooks and will continue to use Redux. Only exception is a quick component that needs useState but that's about it.
Just use mobx and observer. You can combine observables with hooks and the observer component wrapper for FC development and have clean state management. Use the container -> presentation pattern.. make each route in the app a container, use react router V6 object routing to pass observable stores to your containers and prop drill to presentation components so things are easier to test.
Keep presentation components dumb, containers complex.
Use outlets and contexts to handle layout.
I.e create a page that uses contexts to let children update it, do you can have
<Page...
...child route in outlet...
The parent route where page is uses an observer/observable, Content uses context to access the pages mobx observable so it can add content to the page.
Mobx memoizes components automatically for you.
I like it way better than redux.
Optionally you can use custom hooks to grab contexts for parent observable states. So you could make a hook called "usePageContent" and give it append, replace, etc methods.
You probably don’t need useRef for dispatch if you use the function version of setState:
setState(old => ({…old, key:val}))
🤔Thanks for sharing a great article, I am also using a similar approach but I haven't found anything around devtool for such an approach. I'm curious are you using anything for debugging this kind of similar to redux devtool? Also, what do you think of github.com/nanostores/nanostores?
Lately more in the "zero lib" camp.
In a few words: Wrap child components in
memo()
and create intermediary components to "select" parts from contextsActually you dont need React, you can use regular html and js
I really have the feeling that if someone tell that x is better than redux is because they are talking about the "old way" to use redux and not toolkit
Yes, that's true. I was kinda talking about the "old way". But basically I wanted to show, that you don't need any state management tool - you can create your own with a few lines of code.
I believe if anyone tries Vuex (redux equivalent of Vue) will definitely understand what's wrong with redux.
Vuex logic is far more simple , practical and easy to learn .
It actually makes it fun to use .
I developed a project using Next Js. I got away without the need to use redux by implementing a globals context and other smaller context. Instead of a redux like approach I went for an object oriented one. I created hooks for every functionality of the app: e.g ( a hook for a user that has login, logout etc) . When I am want to use a peace of state I just call the useGlobalContext and get the object I want. I am then able to use every functionality from the object I have created. It helps to create different hooks because it makes scaling very easy.
What do you think about Teaful 😊?
github.com/teafuljs/teaful
I think it's even less boileplate, and you still avoid more rerenders, since it only rerenders if the store property being consumed is updated, and not others.
this aproach will have problems when hot module enabled. I have also developed this kind of lib for Form and FormElements. When you build form there will be no problem but when you change the body (main sate) and hot module updates it the Provider context will lost its value and recreate it from scratch since Form and form element is attached to previous context the render will fail. (using CreateContext from React)
Between what I have in React, as well as something like Recoil, I feel like I have all the tools I need for state.
Redux is not only context provider. It is reliable and easily debugable state structure for your application.
Sure, it is complex and has a lot of boilerplate code even with RTK. But if it is too complex for your app, it means only that your app is not big enough to profit from Redux. Use native state management or simpler solutions like MobX.
The irony though
"So I say, it's quite standard solution and why build a new one, when it already exists?"
Yeah, I said that for a while
Since I started to code with Gatsbyjs I try to used both ways, n there's no sense to use redux in all the code... But.
I'll stick with Redux. Context Api increases complexity
I like your post style as it’s unique from the others I’m seeing on the page. Skatter Crack
So I take it that you never used Redux toolkit and also never had to test the data in your global store in one big project?!
I've used REDUX about 2 years ago and now I'm working on project where the redux is used, but basically have it's there as a legacy and are trying to get rid of it. So I haven't used it yet. And store based testing? No, honestly I've never had a use case for it. We either were writing unit tests for small components or using cypress for testing the app as a whole.