Hacker News new | past | comments | ask | show | jobs | submit login
Reasons to Learn Redux as a JavaScript Developer (robinwieruch.de)
120 points by fagnerbrack 20 days ago | hide | past | web | favorite | 145 comments

I'm a Redux maintainer. I agree with the points Robin made, and want to toss out a few additional bits of info.

There's been a lot of chatter on social media over the last couple years suggesting Redux is dying or no longer needed. While it's true that there's a number of other options available, and that there's not as much _need_ to use Redux right away as there used to be, there's still plenty of good reasons to use Redux. In addition, Redux continues to be very widely used, and I've seen quite a few major apps choose to use it over the last year. I talked about these aspects in my Reactathon 2019 talk on "The State of Redux" [0] and my post "Redux - Not Dead Yet!" [1].

We recently released a new official package called Redux Toolkit [2], which includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once. It's now our recommended approach for writing Redux logic. It's also written in TypeScript, and the API is designed to provide a great experience in TS apps.

We're currently working on an ongoing major rewrite of the Redux core docs [3], which has several goals: updating content to better match today's target dev audience (removing references to "Flux", etc), simplifying explanations, encouraging easier patterns, and adding additional content and guidance on real-world usage.

As part of that, we've added a new "Style Guide" page [4], which has our recommended patterns and practices for writing good Redux code. We're specifically emphasizing and recommending patterns that simplify the amount of code you have to write, like using Immer for immutable updates, using the "ducks" structure for files and logic, treating actions as "events" rather than "setters", and so on.

Earlier this year, we also released new versions of React-Redux. v7.0 was a complete rewrite of the internals to improve performance, and v7.1 added a new React hooks API [5] as an alternative to the classic `connect()` API.

If you're interested in some of the behind-the-scenes details on how these libraries have evolved, see my posts "The History and Implementation of React-Redux" [5] and "Redux Toolkit 1.0" [6].

If anyone's got any questions, ask away!

[0] https://blog.isquaredsoftware.com/2019/03/presentation-state...

[1] https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet...

[2] https://redux-toolkit.js.org

[3] https://github.com/reduxjs/redux/issues/3592

[4] https://redux.js.org/style-guide/style-guide

[5] https://blog.isquaredsoftware.com/2018/11/react-redux-histor...

[6] https://blog.isquaredsoftware.com/2019/10/redux-starter-kit-...

In the official FAQ it says something to the effect of “You’ll know when you need redux.”

Recently there’s some backlash surrounding the use of redux and I’d be interested in hearing about projects (preferably open source so we can see the code) that were using the built in react state management tools and came to the conclusion they were not sufficient.

WordPress's Gutenberg editor is built on top of Redux: https://developer.wordpress.org/block-editor/packages/packag...

This is disappointing. Partially for bikeshed reasons, but also for practical ones. Redux thunk is an anti pattern as far as I'm concerned but you're including it by default? Why? Just because it's the first (bad) decision people make when trying to handle async code doesn't mean it's the one that should be canonized.

The createAction function is unnecessarily verbose and the createReducer function obscures more than it abstracts.

The huge bikeshed thread in the replies to this comment is why I’m glad I don’t use Redux any more. It’s an event framework that doesn’t handle asynchrony? Why? Redux makes you write a bunch of boilerplate to do the easy stuff, then doesn’t give you any tools to do the hard stuff.

As I've said elsewhere in the thread, the Redux core is deliberately unopinionated. Redux Toolkit is our opinionated wrapper around that core that provides the best defaults for common cases and removes that "boilerplate":


What solution do you use for async Redux code?

Handle async stuff in mapDispatchToProps.

  dispatch => ({
    async onRequestFnord(arg) {

      const result = await service.fnord(arg);

Or something to that effect.

Which is 95% identical to a thunk syntactically, except that you've limited how that logic can be tested and reused. There are valid arguments against using thunks [0], but all you've done is put the exact same logic in a different form. Hardly an argument against using them.

All you have to do is change it to:

    const onRequestFnord = (arg) => async dispatch => {
        const result = await service.fnord(arg);
    // later, in the component
    const mapDispatch = {onRequestFnord};
At least thunks also give you access to `getState`, and if you need it, the ability to inject an "extra argument" that might contain something like a service reference that can be mocked for testing.

[0] https://blog.isquaredsoftware.com/2017/01/idiomatic-redux-th...

> At least thunks also give you access to `getState`, and if you need it, the ability to inject an "extra argument" that might contain something like a service reference that can be mocked for testing.

I've found this to be absolutely required, due to users being able to trigger multiple ajax calls that could return in the wrong order. You need getState() to know which one is valid and discard the old results.

I feel like that logic is better handled at the reducer level. Trying to think of an instance where it would be absolutely necessary to track state like that in the action. I'm probably missing something, what would be an example?

One reducer holds the user's criteria, another holds the ajax results. The second one can't access the first one's state to confirm if these results are the correct ones to store, it has to be done in a thunk in the action.

Fair as an escape hatch, and I don’t know your situation, but in a lot of cases I think that’s a smell indicating that you need fewer reducers.

I totally understand that there are legitimate reasons for things to end up that way, but it’s certainly something I’d want to avoid or make as rare as possible.

...so how would you handle the case I described above, where a user can trigger the same ajax with different parameters, fast enough that multiple calls can return in the wrong order (due to network latency, server load, the second one being cached, etc)?

I wouldn't use separate reducers for the ajax results and the user's data. Just pass the necessary data from your ajax call along in your action payload and perform your data accounting in the user reducer.

Except you've added a library. And the function is no less testable than the thunk.

I fail to see why adding access to the entire application state is an advantage. This is just cargo culting. In fact that you held up that counter example as a preferable practice tells me everything I need to know about your library.

If you feel so bad about including a "library", the redux-thunk middleware is trivial to implement yourself in about 6 lines of code.

I find your concerns very confusing.

In order:

Thunks are the most widely used middleware by a large margin [0], and we have frequently received complaints that there is no async middleware included in the Redux store by default. Thunks were in fact part of the store during the original development phase [1], but were made separate once the middleware API was defined [2]. Thunks are effectively the minimum viable way to write async logic that has access to interacting with the Redux store, so we include that in RTK out of the box to make that possible.

Sagas, observables, and other similar middleware are far too heavyweight and complex for us to endorse as the default async approach. I also strongly disagree that thunks are an "anti-pattern", and find them to be a highly useful tool (and especially so now that `async/await` syntax makes it even easier to write more complicated async logic in a readable form) [3].

You are always welcome to add another middleware or disable thunks as part of the store setup process instead if you want.

I don't see how this:

    const addTodo = createAction("todos/add")
is more verbose than this:

    const addTodo = (text) => {
        return {
            type: "todos/add",
            payload: text
We've had a version of `createReducer` shown in the "Reducing Boilerplate" docs page [4] for years, and there are literally hundreds of similar "lookup table"-type utilities out there. Including this in RTK is a no-brainer. It's even more valuable, because ours uses Immer inside to let you write "mutative" immutable update logic instead of having to nest spread operators. I don't see how this is "obscuring" anything.

Finally, our recommendation is that most of the time you shouldn't even be calling `createAction` and `createReducer` yourself. Instead, you should be using the `createSlice` function [5], which automatically generates action creators and action types for you, and calls the other two functions internally.

[0] Slide 38 in https://blog.isquaredsoftware.com/2017/09/presentation-might...

[1] https://github.com/reduxjs/redux/issues/1

[2] https://github.com/reduxjs/redux/pull/63#issuecomment-110575...

[3] https://blog.isquaredsoftware.com/2017/01/idiomatic-redux-th...

[4] https://redux.js.org/recipes/reducing-boilerplate#generating...

[5] https://redux-toolkit.js.org/api/createSlice

Thunk is not a proper solution to the problem - it's a totally open escape hatch! You lose ALL the nice guarantees and behavior of redux when "action" no longer means "immutable, serializable event" and starts meaning "completely arbitrary function call with unbounded async effects."

There's no functional difference between using redux-thunk and just calling a "global" `dispatch` in random callbacks; both are equally unmaintainable in the face of interacting effects.

EDIT: Having a bunch of complex async logic all modifying your store in overlapping ways without careful coordination is like having concurrent operations on your database without any transactions or locking.

Actions are _still_ always an "immutable serializable event". The async logic is going to live _somewhere_, either in your components, or outside of them. Thunks are a way to move that logic outside of your components. The end result of the async logic is still dispatching plain object actions.

...contrary to the other replies, I think thunks are fine (and have yet to understand why sagas are needed at all). But I have minor issues with createAction and createSlice:

createAction I don't think we'd ever use, and I'd recommend against it - multiple explicit function arguments instead of a payload blob is much easier to understand when reading the code, and the action function should use those arguments to create the blob. Your example here makes sense simply because the blob appears to be a single string value, rather than say, id+value, or even something more complex.

createSlice, to me, appears to be adding a lot of complexity for no apparent benefit. I even had to read through it a second time before I realized extraReducers was the escape hatch to avoid the action=reducer antipattern, but the structure definitely still seems to be promoting it, where actions are defined as part of a set of reducers, then reused elsewhere if need be.

By default, `createAction` accepts a single argument, which it then includes as `action.payload.` This requires the least amount of code written to define the action creator, but requires the dispatching code (such as a click handler) to correctly format the payload contents. It aims for the 80% use case by default.

However, yes, there are many cases where you might want to accept multiple arguments for an action creator, do additional work to define the contents of the payload, or generate unique IDs as part of creating the action. That's why `createAction` also accepts a "prepare callback" as its second argument [0] [1].

`createSlice`'s primary benefit is to remove the need to write action types and action creators by hand. All you have to do is write your reducer functions, give them meaningful names, and you get the action generated creators for free (and the action types effectively become an invisible implementation detail).

We've always told folks that it is not required to have a 1:1 mapping between action types and reducers [2] and that many reducers can respond to a single action [3], but realistically speaking, most actions _are_ only handled by one reducer. Again, `createSlice` handles that 80% use case by generating actions that correspond to that given case reducer in the `reducers` argument, but also allow you to handle the "events" case by listening for other actions in the `extraReducers` argument. (And, the `reducers` argument allows defining "prepare callbacks" as well if desired [4]).

So, in both of these APIs, we solve the 80% use case by default, but allow handling the additional 20% if desired.

Most folks won't need to be calling `createAction` themselves given the existence of `createSlice`, but there's no reason to _not_ have it as part of the public API (especially since `createSlice` already uses it internally).

[0] https://redux-toolkit.js.org/api/createAction#using-prepare-...

[1] https://redux-toolkit.js.org/tutorials/intermediate-tutorial...

[2] https://redux.js.org/faq/actions#is-there-always-a-one-to-on...

[3] https://redux.js.org/style-guide/style-guide#model-actions-a...

[4] https://redux-toolkit.js.org/api/createSlice#customizing-gen...

Async middleware is an anti-pattern. Take care of async stuff in mapDispatchToProps.

Actions are ALWAYS action(payload) => { type: action, payload }

There's no reason to call a function everywhere you need a new action. Just define all of them in one place by listing the types. You can easily write a function that looks like

  createActions(['TODOS/ADD', 'TODOS/DELETE', ... ]}) 
That returns all your action creators and types in one place. Import them as needed.

The above single call function to create a todo action is verbose without achieving any efficiency. It's bad design.

Don't get rid of the switch statements at all. Use them. They're clear. If you need to slice things up, just use child reducers in the switch branches. Lookup tables are less performant and don't buy you anything.

Sorry to be so harsh but this kind of cargo-cult best practice stuff is how you end up with Javascript that feels like Enterprise Java™ from back in the 00s.

Yeah, we'll have to disagree here.

We officially recommend consistently using action creators [0], and I talked about the reasons to use them in a blog post [1].

Switch statements and lookups are almost completely equivalent (except for fall-through and default case handling) [2], and "performance" is irrelevant here. In this case, use of `createSlice` means that writing the lookup table object generates the resulting action types and action creators for free, which drastically reduces the amount of code you have to write.

I am strongly against defining async logic inside of `mapDispatch`. It's similar to writing thunks in the first place (an arbitrary function that has access to `dispatch`), but it's less testable, not reusable, _and_ doesn't have access to `getState`. In fact, you should be using the "object shorthand" form of `mapDispatch` [3] to pass action creators into connected components, like `const mapDispatch = {addTodo, toggleTodo}`.

The advice to put async logic in `mapDispatch` also does not help if you're using our new React-Redux hooks API [4], where you just have `const dispatch = useDispatch()`.

The Redux Toolkit API is specifically designed to remove as much as possible of that incidental complexity from your Redux code, and move it _away_ from being that "Enterprise Java" feel.

[0] https://redux.js.org/style-guide/style-guide#use-action-crea...

[1] https://blog.isquaredsoftware.com/2016/10/idiomatic-redux-wh...

[2] https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-ta...

[3] https://redux.js.org/style-guide/style-guide#use-the-object-...

[4] https://react-redux.js.org/api/hooks

You misread. I wasn't railing against action creators, I was saying your method of generating them was bad.

So yes. We disagree strongly but it's because you are outright wrong in your claims. Putting function calls in mapDispatch does not do anything to their reusability or their testability. They're still functions. You're making taste claims but treating them like they are factual.

If you're wondering why my tone is so dismissive it's because yours is so prescriptive. I've worked on a lot of react and redux projects and I know my way around. If you're attempting to reify your development taste into a framework of "best practices" you need to be prepared for criticism. Responding to me with footnoted appeals to authority does nothing for your argument.

FWIW, I'm the primary Redux maintainer, have written large chunks of the current docs (including the FAQ and the Style Guide), have overseen the development of the last three major versions of React-Redux (including writing v7 from scratch myself), and drove the entire development of Redux Toolkit. So yeah, I guess you could call that an "appeal to authority".

The docs content and recommendations are based specifically on the questions I've seen asked, the ways I've seen Redux used, and the concerns I've seen expressed by users. My goal is to simplify how people learn and use Redux, and point them in directions that lead to better codebases. You're always welcome to use other approaches if they work better for you, but there's good reasons for all the patterns we're recommending.

Exactly why I'm frustrated with you. You're doing more harm than good and you're able to do it because you are an open source maintainer.

Redux is

  (state, action) => state
Sure, wiring it up is clunky without a few helper functions and you're right there should be a set of reified best practices. Unfortunately, the ones you are holding up simply do not buy the developer anything and serve to make the js ecosystem even more impenetrable than it already is.

I'm only angry because I know that I'll be wading through piles and piles of garbage as a result of this library.

This gets to a bigger problem with the javascript ecosystem. Someone writes a useful library (s, a) => s, then a hundred other people decide to create "opinionated" frameworks to manage that library. What you're left with are hundreds of arbitrary practices yanked from inside development organizations. They almost always obscure the thing they're trying to illuminate.

Opinionated frameworks are only effective if you get them as a whole package. Rails works because it's every single thing you need for a web app rolled up into one suite of libs. Opinionated Javascript frameworks have been unable to mimic this because the big ones are focused on narrow domains: ui rendering, state management, etc.

This creates an explosion of cargo culted opinions-turned-libraries, which are basically what we had in the 00s with all the Java libraries that were design patterened to death. Believe me, there was a lot of citing going on in those discussions too.

This library will do more harm than it does good if people buy in.

I'm always curious when people build new js libraries with new terms. Did you consider just using the terms of Design Patterns like Command instead of Action and Observer pattern instead of Reducer?

There were numerous bikeshed debates about naming early on in Redux's development, and the decision was made to keep using Flux-related terminology because that's what folks at the time would be familiar with:

- https://github.com/reduxjs/redux/issues/137

- https://github.com/reduxjs/redux/issues/351

- https://github.com/reduxjs/redux/issues/891

Your first link actually shows Dan deciding that the flux-based "store" name should be changed to the JS-based "reducers".

Any reason why Dan isn't contributing to your new special toolkit, by the way?

Dan stopped actively maintaining Redux in the summer of 2016, when he handed the keys to myself and Tim Dorr. This coincided with him working full-time on React.

He's occasionally dropped by to comment in an issue and leave an opinion (such as when he suggested we drop `useActions` from the React-Redux hooks alpha), but he no longer does any work around Redux.

Redux was such a mental overhead for me when I started out with it. The amount of boilerplate code that I had to write for the project I worked on was very frustrating. What helped me truly understand the concept was Elm.

These days I ask junior devs to learn Elm for a few days. We still use Redux, I will not choose it for new projects.

I see the Redux devs defending their tool in every comment thread around Redux and I have to say to them: it's not your fault and there's not a lot you can do to fix it by yourselves. The problem is every tutorial and training and boot camp and video I've ever seen around Redux treats Redux like heavyweight boilerplate and teaches you that you need five files with Redux where you only needed one with plain React and every function you add needs to touch all five of those files, including a file that only exists to write the name of the function you've added.

What I hear from the Redux devs does not in any way line up with what I heard from Brad Traversy or Stephen Grider, who have taught tens (hundreds?) of thousands of people on Udemy that the simplest React projects must be used with Redux and Redux turns beautiful React code into dotcom-era Enterprise Java.

Yeah, unfortunately once something gets a large community, it takes on a life of its own.

Part of the issue is that the Redux docs _do_ show many of these patterns (`const ADD_TODO = "ADD_TODO"`, separate files and folders for constants / actions / reducers, etc). Dan has said several times that he didn't expect folks to literally mimic everything that was shown in the docs, but tbh that's what people do. He's also said he wishes he'd emphasized teaching it in some different ways.

We're doing everything we can to try to teach better practices and patterns now, but that can't magically change the gazillions of existing tutorials and codebases that are out there.

> didn't expect folks to literally mimic everything that was shown in the docs

Isn't that the point of documentation? To give you a reasonable, suggested way of incorporating a library into an existing project? This is almost saying, "the docs were wrong and everyone learned the wrong thing". I don't think you can blame package users for that. People reference documentation to know what to do, and also what to avoid, when using a new library.

Yeah, I know. I'm not blaming Dan here, specifically, just repeating what he's said several times himself.

That's why we're doing the docs rewrite. The target audiences have changed since 2015 (new devs learning Redux in a bootcamp vs folks coming from other Flux libraries), we've got a better understanding of how Redux should be used, and we want to set folks on a better path from the beginning.

People need standardization. The docs are usually the source of truth or go-to for any disputes. Whatever's written there is the best practice since literally everyone who uses it would have read it.

I'm the mentioned Stephen Grider - to clarify, I don't advocate using Redux on every project by any means, and I apologize if I said anything in my course to that effect.

Instead, I advocate using Redux on larger projects - especially as a beginner - because it naturally provides some structure. There is a large community of engineers that advocates using as few libraries as possible. I generally agree with that sentiment, but not for beginner engineers. A beginner engineer doesn't have the skills necessary to architect a large project from scratch (hence the 'beginner' designation), and can benefit from being given a bit more rigid structure.

And that's a valid approach, I learned React (and unfortunately Redux) from your courses and they're very high quality. I really do appreciate the handful of your courses I've bought and owe you a lot more than the $10 I paid for the Udemy video.

However, it is exceedingly difficult to find any React tutorial that doesn't have Redux, making it seem to beginners that Redux is required for React. And Redux is entirely overwhelming, especially for a newbie like when I was learning React. It took me three or four courses until I was comfortable and knowledgable enough to strip Redux from my projects, and after that my speed and productivity skyrocketed.

When I was first learning React from your video, I'll admit I turned it off several times, frustrated with the mindset that I was never going to learn React and React was stupid because what do you mean I need to hydrate a store and map state to props and maintain a file listing all of the functions I have? Turns out it's just Redux that I hate. React is fine. And without experiencing the pain that Redux solves, Redux (to me, as a beginner) just seems like completely unnecessary boilerplate that keeps me out of the React ecosystem.

Anyway that's just one person's feedback. I'm sure Redux solves some very real pains, but to me as a beginner, Redux is more of a pain than the problem it's designed to solve (because I've never experienced the problem it's designed to solve) and it is very difficult to find a high-quality course or demo project that doesn't include it.

FWIW, our recommendation has always been to focus on learning React first, and _then_ learn Redux once you're comfortable with React. That minimizes the number of new concepts you're learning at once, clarifies the differences between what React and Redux are and how to use them, and helps show why Redux might be useful alongside React.

Which leads nicely back to my statement of

>What I hear from the Redux devs does not in any way line up with what I heard from Brad Traversy or Stephen Grider

This perception will continue (and only get worse) the more junior devs are educated in trainings/bootcamps that require Redux in your first project. This is the reason so many people have such negative reactions to Redux, they're not yet comfortable with React (which is a new way of thinking by itself) before Redux is added into the mix and it gets very overwhelming very quickly.

OTOH, any sufficiently complicated React app contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Redux.

OTOOH, react et al are just attempts to make the asynch web synchronous.

Request/response cycle returns a resource, including embedded resources, and we get over it and move on in life.

Now, how on earth a folder-index-sharing protocol for hyperlinking became the planets default UI surface for everything ever, that's another ball of wax to fry another day, killed with the same stone as the mythical 2 birds, etc.

Please try out our new official Redux Toolkit package, which will help simplify your Redux logic considerably:


Thanks, will check it out. Unfortunately, it only adds to the dependency hell in the front-end stack. We already use quite a few add-ons to make Redux+Sagas tolerable.

My personal take on the whole JS + Redux stack is that Redux tries to bring order to a language which fundamentally doesn't support certain traits - immutability, custom types, etc. While commendable, it will always just be a band-aid. I wonder if it's time to move to more purpose built languages which do not have these shortcomings (Elm, PureScript, etc)

That said, I'm sure Redux and friends will continue to be used in the years to come.

Right see, this is what all redux proponents advocate. Every redux project I see is chock-ful of "extras" to make redux workable, and this changes from season to season. If this isn't an architectural smell, I don't know what is.

Not sure why different libraries would be an architectural smell. Not only Redux projects vary greatly in what they bolt onto their state management. I've used Redux successfully on its own. The dataflow pattern it mandates is important, not the concrete implementation.

Redux itself is a very simple concept. There is so much boilerplate because the actual library makes very few opinionated decisions. The issue is that people are looking for something comprehensive from what is supposed to be a minimal base library which you can use to build a huge variety of other things.

You can use the built-in IoC controller or Autofac or ServiceStack or whatever you want along with ASP.NET Core projects. I don't think it is architectural smell at all.

Also I find classifying people as Redux-proponents funny. It's not a camp, or at least not supposed to be one.

That link is a 404, looks like the site is here: https://rematch.github.io/rematch/

It's such a shame that redux missed the equivalent of returning a Cmd in reducers. It would have made everything much simpler and reasonable, especially the boilerplate.

The issue with Redux is that a dispatch() and hence a state update causes a synchronous (hence blocking) update of the view.

Andrew Clark better explains the difference in this tweet: https://twitter.com/acdlite/status/1195453776217296896

> Redux architecture depends on the ability to block the main thread until rendering is complete. Once you make rendering non-blocking, your store is no longer synchronized with the UI. Leading to data races and inconsistencies.

Many view layers are moving towards non-blocking rendering as they need to account for things such as different priorities based on what the user needs: for instance a user typing needs larger priority than a banner showing.

Once it is non-blocking it's hard to keep a store and several UIs elements in sync. For instance your store might say isLoading:true but the UI might not show that since it's rendering/working another higher-priority element on the screen. And then you might have another view that depends on the isLoading:true which causes another thing to appear on the screen before the other element is shown... all this leads to inconsistencies and race conditions.

Essentially the problem is that we have lots of code that does (especially in thunks):

  const state = store.getState() 
  // here we expect state to contain changes done by FOO
  // but with non-blocking UI this is no longer the case

Andrew's not wrong, but I'm also not entirely convinced it's as big of a concern as he's describing.

For context, Andrew is a co-creator of Redux, and is a current React core team member. He's spent the last couple years working deeply on the "React Fiber" rewrite that became 16.0, and the "Suspense" and "Concurrent Mode" features that are now available in alpha.

He's definitely right that Redux's synchronous update model is somewhat limited compared to how React's "Concurrent Mode" needs to be able to dynamically re-order state updates based on priorities. But, it remains to be seen how much of an actual problem this is in practice. Andrew's been heads-down in this feature for a long time, and so it's natural that he's seeing everything through that kind of a lens.

We've got an open issue to discuss and analyze how React-Redux might be able to work better with CM and Suspense:


Isn't the issue here that a view is causing something, instead of the store?

If a view would just render and dispatch messages on user events, then this wouldn't really be an issue, as far as I understand.

The store and "runtime" can take care of loading stuff, independently of being rendered asynchronously, or not.

Redux is tremendously verbose though. Message passing isn't attractive when the state space is big.

That's specifically why we've created our new official Redux Toolkit package - to simplify the incidental complexity that is not actually part of Redux itself. Try it for yourself :


Agreed. I much prefer the style of rematch[1] where you bind directly to the actions rather than creating a constant to check for in a switch statement

[1] https://github.com/rematch/rematch

MobX does not get enough love and has been around for a while


MobX doesn't get enough hate either. Inexperienced devs will put everything in the global state and end up with a soup. And, as opposed to Redux, it is _magic_ - I would have trouble rewriting it from the docs, would you be able to do it? Note that it also changes the way React render method works. All in all - no thank you.

Sloppy developers are sloppy in redux as well.

Vuex has a nice pattern with plugins and modules.

Mobx stands on its own outside of react. I understand many people don’t consider redux outside of react and don’t talk about one without the other. As other comments mentions the mobx state tree is more a drop in replacement for redux. I am unfamiliar with the render method changes you reference- do you have more info or a link?

> Sloppy developers are sloppy in redux as well.

Agreed. But the mess they make in MobX is in my experience much more difficult to clean up.

What I meant with render method changes is that mobx tracks access to state during a call to render (which means that anything that might influence rendering, must be accessed inside render). It seems like it's not a big deal, but it can lead to surprising bugs if one is not careful. Redux otoh uses props to pass data to render, which is much more explicit and allows regular react dev tools to be used.

I might have some details wrong - I met MobX a year ago with two legacy apps and it was a hell to maintain. Still is actually, I'm just not that involved in these projects anymore. It's magic, and not in a good way. For new projects we use Redux Toolkit, which strikes a nice balance between verbosity and being explicit.

> Inexperienced devs will put everything in the global state and end up with a soup.

And Redux is just one big global state

> it is _magic_ - I would have trouble rewriting it from the docs, would you be able to do it?

I accidentally wrote a (bad) version of MobX's core mechanism before I learned about it. At its heart it just uses object Proxies to intercept getters and setters; the former to find out where something is used, the latter to find out when it changes.

It can feel a bit magical sometimes, but the magic isn't hard to understand, almost always just works, and when it doesn't do what you want it usually causes a performance impact, not breakage.

> Note that it also changes the way React render method works.

I don't know what you mean. Aside from "tracking" which observables get accessed within it, all it does is decide when to trigger updates.

I love love love MobX. It lets me design my data flow however I want - local or global, functional or imperative, and everything in-between - while basically forgetting about pub/sub entirely until it comes time to do spot optimizations. It's removed an entire layer of mental overhead, without contorting the entire app into a complex and boilerplate-y new paradigm.

I found the philosophy communicated by mobx-state-tree to be a lot clearer than redux (particularly stuff around patches, snapshots and async flows): https://mobx-state-tree.js.org/intro/philosophy

Check out Svelte. Essentially reactivity baked into the compiler so you don't have to use observables everywhere.

One of the main reasons I'm using Redux is because of the global store. If I understand the docs correctly this is how you do it in Svelte:


What's up with that? A strange semi-singleton where you need to use writable(0)? Also strange that subscribe return unsubscribe. Doesn't seem very intuitive.

Second time it’s been mentioned in as many days. Are you actively using it?

Yes it is pretty straightforward though the project is still growing and suffering from the usual growing pains. Think docs that has plenty of room for improvement, insufficient example projects and components. On the up side the team responds quickly, the concepts are ridiculously simple compared to Redux and there's a burgeoning community around it.

E.g. https://sveltematerialui.com/

The Next.js equivalent for SSR is Sapper: https://sapper.svelte.dev/

Note the creator of Svelte is the same guy who created the Rollup bundler so pretty much everything comes with dual configs for both Rollup and Webpack.

IDK, it's good to have competition but I could never get warm with mobx, undo is not baked it, more magic, similar learn curve but less real understanding at the end. Then, the biggest bummer, you have also mobx state tree which is too much fragmentation within mobx.

Friends don't let friends use observables

Could you elaborate on this?

I saw enough large scale knockout projects to see how quickly observable based code fold down into a disjoined incoherent mess. At some point it's virtually impossible to debug or support it.

I'm not sure who downvotes me, people who successfully used them in production or who dont and don't know they are in for the ride, but for me this is an insanity and I won't touch mobx even with a six feet pole.

I've been using MobX on a quite large and complex React app for a few years now and I have no regrets. The key, as in all programming, is to make your (observable) state as small as possible. You shouldn't have observables that are derived from other observables; you should have computed values. You should use reactions sparingly and almost never use them to mutate other observables. Etc. MobX lets you treat state as state and then get all the benefits of memoization and reactivity that you want, for free. It only gets hairy when your state itself gets needlessly hairy.

I've been burned by observables in a large scale knockout project too. Definitely left my weary of observables for UI development in general.

I was not a fan of knockout AT ALL. But here we are.

People would get too clever and abstract way too much. I don’t see the same with Vuex and MobX but I can understand why you feel that way based on this reply.

The concept of having a centralized state / store with complex apps is impossible to ignore IMO (even if you just have plain objects holding them). At that point sprinkling in a pattern to standardize access isn’t that much overhead.

What do you use instead?

Redux is a mixed bag but I'd say it's one of the most elegant ways to handle state.

Biggest problem and reason why people (me included at the beginning) don't like Redux, you should know when you need it and you should use some abstraction layer (eg RTK). One cause could be the docs: while the maintainer try their best to educate a lot (also in this thread paired with interesting link building strategies), there is not quick and easy way to get into Redux. If you start with RTK's docs, you don't understand everything. Then, you need to read the Redux docs, then again back and forth. And this just takes too long to grok an actual simple API.

One easy indicator for Redux or not: does your app need kind of an undo feature, then you should dive into Redux and I'll promise you. there is no other way to do this in such a sane, maintainable way.

As mentioned elsewhere in the thread, we're working on a major rewrite of the Redux core docs. If you've got any specific suggestions on how we can improve them, please let me know.

When you say "no quick and easy way to get started", are you referring to code or docs?

In terms of explanations, RTK is currently documented as a separate layer on top of Redux, because that's really what it is. So yes, we do assume that folks have learned what Redux is and how to use it already, and RTK is then a faster and better way to write Redux logic instead of doing everything "by hand".

As part of the core docs rewrite, we do intend to completely redo the tutorial sequence, and we'll be introducing RTK somewhere in the tutorials and encouraging its use.

The first thing I do when teaching someone Redux is tell them that Action just means Event, and "action creator" just means an event dispatcher, which is to say, any function that dispatches an event. Naming the least-active part of the system Action is really confusing—it doesn't even describe an action, necessarily, nor force any action—and naming a function that happens to emit an event an action creator as if it's creating actions (huh? what does that mean?) is misleading.

This is specifically due to the fact that the original Flux implementation labeled these objects as "actions", and Redux was designed as a Flux Architecture implementation:


Is it too late to change it? I’ve worked at a few agencies for a lot of companies using Redux. Teaching people to think of actions as events instead of setters has been a huge pain point.

I would seriously suggest just find and replace the word action with event within Redux and the docs. Maybe have one page explaining the history behind this.

We certainly aren't changing the actual name "action" to "event" in the code or docs.

_However_, we _do_ have a new "Style Guide" docs page, and in that we tell people to "Model Actions As 'Events', Not 'Setters'":


We'll be adding more pages with info on that topic as part of our ongoing core docs rewrite. I'd suggest pointing people to this style guide entry, as well as these excellent presentations that go into more detail:

- https://github.com/dmmulroy/talks/blob/master/event-driven-r...

- https://youtu.be/K6OlKeQRCzo?t=2626 (video) / https://rangle.slides.com/yazanalaboudi/deck#/ (slides)

> We certainly aren't changing the actual name "action" to "event" in the code or docs.

That's what I think should be done, with a page noting that historically they were called actions but events is a better name. I think it's the right move to help people understand Redux better and write better Redux code.

There are still people learning Redux for the first time today and the current docs and the name "action" seems to push them towards writing imperative code.

IMO imperative actions are the biggest issue people have using Redux.

I guess a first step in the right direction might be just redoing the docs in an event style. e.g. The counter example would use events like "clickedIncrement" and "clickedDecrement".

As long as I'm giving feedback on stuff, I really like createReducer (builder api) and createAction from Redux Toolkit. I think createSlice encourages the action = reducer anti-pattern.

I meant the docs. It's like decades ago like when you wanted to learn Rails. Then somebody said stop! First learn Ruby... This is not how people learn. There's too much time between learning and trying, so there's no proper and face-paced feedback loop. Let's try to get into somebody's head who's ok in react and slowly wants to get into state management but having no clue of nothing:

His mind:

- Ok, where should I start?

- Which is the best? Mobx, redux, just ContextProvider or local state??

The poor guys falls into the rabbit hole and reads Reddit, HN, Github issues for hours, still not sure what to do; 2 hours later

- Ok, let's try redux, for whatever reason, the maintainers seem to be quite active

The poor guy tries to decide if he should go just with redux/react-redux or rtk; former because it is good to understand the foundation, latter good because just to avoid boilerplate and that's what this acemark is promoting everywhere. The guy is still overwhelmed and don't forget doing decision is hard; it's one of the biggest struggles for humans

- Ok, I guess I need to start with redux because it's the foundation.

He sees all the theory, new terms, the boilerplate and there's no fast-paced feedback loop; he just think heck and checks out mobx, 4 hours later

- Man mobx is even more weird, totally unstructured and while mobx's maintainer seems to be a nice guy, my gut feeling is: stay away

- Let's check out npmtrends, oh great, npmtrends shows that people also don't like mobx, cool, it has much less traction, decision made, nice

- So, let's try redux again, maybe this time I start with RTK

We know what happens, RTK has better feedback loops but w/o the basic knowledge of redux he will struggle, 6 hours later after jumping back and forth the both docs

- Lets stop here, tomorrow is another day.

He is out of flow, motivation is down, the next day he doesn't continue because the whole experience didn't feel good, he writes a post on HN that redux sucks steel and maybe looks the next week again on it

The situation is a bit like with kubernetes; until you have proper feedback loops with k8s you need days, with new k8s distros this got better but the biggest bummer are the k8s docs, too much, too verbose, so many new terms, no feedback loops at all and every k8s examples has minimum 100kb yamls.

What would be great and I know that writing good docs is much harder than actual code:

One and only piece which the reader should read first, which teaches him redux foundation, react-redux boilerplate and rtk within max 2 hours. After that he should have a working example AND should have understood it. No links to other resources, no nothing.

This main piece should be linked everywhere in the Github readmes of all redux relates projects in bold and h1 READ OUR MAIN PIECE FIRST, on all the doc pages and in all forum posts. I mean look at your post's footer in this thread: so many links, why? You need one entry point. RTK could be the entry point, it just needs a bit more flesh on redux and should be declared as the entry point of redux. There's one disadvantage though: with RTK as entry you'd kill the ecosystem around redux but IDK if you did this anyway by declaring RTK the standard way. And you would have a lot of redundant docs with redux/react-redux. Or maybe we need to merge all because maintaining redundant docs is just too much for an OSS project.

Whatever, all not easy but there must be a way because redux is the most underrated thing in the frontend world, it's def the best state management and RTK is a nice and long deserved abstraction.

Great and substantive comment. Docs should be example and results driven or they're not really docs. Django and now Rails do a good job of this, in part because they're frameworks. But even for something of the size of Redux, it would be fantastic to see a basic mobile todo app with a navigation bar and a basic logged in/logged out mode built from scratch to show you how to manage state in the real world.

Thanks for the feedback. I've pasted it into our "Docs Rewrite: Overview" issue for reference [0].

Responding a bit further:

There's not much we can do about folks going back and forth between Redux and various non-Redux-y things. That's outside of our scope and ability to help with.

Teaching-wise, there's several reasons why the docs are structured the way they are right now:

- Redux itself is an independent library that is not tied to React in any way (and is used with other frameworks / UI layers as well). Our docs do mostly assume that you're using React, but the tutorials start out trying to show how you'd use Redux without any UI layer, to teach the basic concepts.

- React-Redux has been a separate library from Redux itself since it hit 1.0. While we do teach the basics of how to use React-Redux in the Redux core docs, it has its own separate repo and separate docs site. Similarly, RTK was designed as an addon layer around the Redux core, so it also has a separate repo and separate docs site, and its docs assume that you understand the basics of Redux already.

- That said, yes, the vast majority of Redux usage _is_ with React, and we are encouraging folks to use RTK as the default way to write Redux logic. So, it's very reasonable to have a docs page that shows how to jump right into using both of them as fast as possible. This is something I've already said I want to add to the docs, as a "Quick Start" page [1].

In terms of teaching flow and learning, there's various ways to approach explaining things. Dan Abramov wrote the original Redux docs content and structure, and his teaching style is very much "bottom-up, from first principles". You can see that style in pages like the current "Middleware" tutorial page [2], and his Redux videos on Egghead [3]. That approach works great for some people, but other folks just want to be shown _how_ to do something first, and learn the "why" later. It's hard to serve both groups at the same time.

One issue with teaching RTK first is that while Redux requires / expects that you use immutability correctly, RTK's use of Immer does hide the fact that immutable updates are happening in the background. It would be easy for someone who learns Redux via RTK to assume that writing mutating logic _is_ the right way to write reducers, not realize that it only works right due to the "magic" inside RTK's APIs, and then go write reducers in another project without RTK that are completely buggy as a result. I haven't yet entirely figured out how to handle that aspect, other than plastering warning notes all over the docs. That's one reason why I would prefer that folks understand the core principles first and how to do things by hand, and then switch to RTK to write the same code in simpler form.

As I said earlier, there's good reasons why we have separate libraries and separate docs sites. I can hypothetically imagine pulling things together into a combined docs site, similar to how NgRx does it [4]. But, I'm not sure how technically feasible it would be. It might require restructuring everything into a mono-repo, and I don't think I want to have to figure out how to migrate everything at this point.

All that said, yes, part of the goals for this Redux core docs rewrite is to provide a much more obvious learning path.

Finally, no, I don't think RTK is "killing the ecosystem around Redux", as the ecosystem is much broader than what RTK includes. RTK does obsolete a lot of similar libraries, but there's many other pieces out there for other use cases.

[0] https://github.com/reduxjs/redux/issues/3592#issuecomment-57...

[1] https://github.com/reduxjs/redux/issues/3594

[2] https://redux.js.org/advanced/middleware

[3] https://egghead.io/series/getting-started-with-redux

[4] https://ngrx.io/docs

Thanks for your reply. Man, so much legacy, now I get it better. The Quick Start sounds like a good next step and some low hanging fruit!

ps and to understand your That's outside of our scope and ability to help with. better: Are you not also maintainer of redux and react-redux?

Yes, I'm a Redux maintainer. My point is that I can't do anything to prevent folks from asking "what state management approach should I use?", looking at NPM trends, reading other tutorials that espouse differing approaches, or jumping back and forth between the React, MobX, and Redux docs sites. All we can do is try to provide as much helpful info in our own docs, and even that assumes that folks even _read_ the Redux docs. I see a lot of people asking "I want to learn $TOOL, what's the best course on Udemy?", and not even looking at the actual docs for $TOOL at all. Doesn't matter how good the docs are if people don't read them :(

Here's the library I wrote to replace redux for myself: https://github.com/gunn/pure-store

It's the simplest API I could think of, has excellent type support, and handles immutability for you.

Thank you for pure-store, I'm surprised it hasn't attracted more attention!

The API is perfectly small and straight-forward, it's close to the ideal I imagine.

For a few years I've been using a similarly tiny library I wrote to reduce Redux's verbosity, but I may migrate to pure-store as it's even smaller and gets to the essence of state management.

Seeing the popularity of React hooks and Redux+friends, I know I'm in the minority of people who lament the direction they're going. I'm secretly hoping for a "destructuring" of the React ecosystem into more generic concepts/libraries/modules that work with or without React, carrying on the lessons learned but implemented as simple and independent as possible.

To me, pure-store (and immer for sure) is one of those libraries that point the way to a better future.

Looks very similar to Undux! https://undux.org/

Then again, who hasn’t built a state management framework for React?

Nice. My take on the simplest possible API:


This horse has already been beaten into a bloody pulp, but Redux is so unnecessarily verbose. All due respect to the team behind it, but the DX of Redux is just horrible. I can only imagine how many developers simply gave up and became farmers because of Redux.

After being subjected to the "action types" modules full of redundantly named constants, having to touch five files for a small feature, I simply use Rematch now. Its API is like cake compared to Redux, while it still satisfies the same underlying pattern for consistency/testability.

"I'm going to tell my kids that Rematch was Redux"

As I've pointed to a number of times throughout this thread, we created our new official Redux Toolkit package specifically to address those concerns:


In addition, our new Style Guide docs page recommends patterns that avoid those issues as well, such as using the "ducks" file structure:


Anecdotally, Redux was absolutely awful to work with. Want a project chockful of middleware to make it passably usable? Use Redux. With all due respect to the Redux team, I simply don't think Redux scales in large applications. The "defaults" are insanely unproductive and encourage developers to cram tons of state in some global pile of state-soup just to claw back conveniences they lost. Monadic transformers and pure state transitions - great ideas in principle, carried way too far here in an ecosystem where the rest of the language and libraries rely on and use mutable state. Not to mention the lost performance, bloat for code that would be considerably shorter, and more disadvantages.

No thanks Redux, no thanks :/

> With all due respect to the Redux team, I simply don't think Redux scales in large applications.

Very much disagree. Redux, to it's, credit, almost made development boring to me in the sense that we had an unreasonably large application that could scale and scale and scale without any concern whatsoever. I've worked with other patterns and libraries since and nothing compares to how easy it was for our team (of varying degrees of experience) to jump in anywhere and make additions that made sense through time. And like the author of the article mentions, all of our devs grew in general because of what they learned from it's patterns / concepts.

Interesting. I didn’t write a big app with Redux, but one potential scaling issue I found is this:

You kind of have to think about the structure of your state tree beforehand. If you later discover that you sliced your state in several sub-trees with respective reducers, but the reducer of sub-tree A now needs to get some info from sub-tree B, things can get confusing.

Or put another way: if you think parts of your state are independent from other parts and this later turns out to be wrong, you need to refactor quite a bit or have to live with an improperly separated state.

I guess scalability depends a lot on the nature of changing requirements and how experienced you are to define the state tree. This is actually something where I see the biggest learning curve.

For example: say you pull data from your database. This could be considered the normalized state and everything else is just derived from that state. However, in your UI, it doesn’t make sense to work directly with the original normalized DB state, but some kind of semi-normalized intermediate state.

Furthermore: you could put computed/derived state directly in the state tree, or use something like the Reselect library.

Since there are endless possibilities to design this, it can be tough to learn any of this. Most tutorials also deal with very simplistic state trees.

I assume you are referring to React apps? I am curious how you manage state in large React apps. I've yet to find a solution in large/complex apps that I truly like. But in general I have found I prefer "redux-esque" approaches.

Redux itself has had a pretty rocky road. From its early days of lots and lots of boilerplate, to "ducks" and all kinds of other things over the years. I think the problem was Redux has always been nice in theory, but getting it nice in practice has literally been years of people trying all kinds of things. I think most people's opinions of Redux are largely shaped on at what point they jumped in.

I am having some decent success with the new redux-toolkit. It's finally an opinionated redux offering, which is nice to see. I've also been exploring mobx-state-tree and rematch, and have even had some success with using context coupled with useReducer for a very lightweight redux approach.

The biggest problem with Redux is its complexity. I have seen first hand how Redux scales in large web applications and it sucks. Ask anyone who works at Atlassian and worked on the new Jira, they had to basically break each part of the app up into micro-applications with their own stores because a single-store in Redux for large scale applications does not scale. The navigation sidebar is its own damn React app with Redux store to get around the limitations of a massive store.

If you're looking for a Redux alternative, look no further than RxJS. You get all of the power of Redux without the complex concepts to learn. The Aurelia Store plugin is a great example of using RxJS for state management (it also works with Redux dev tools). A lot of Angular developers will also tell you they use RxJS for state and attest how great it is.

I have seen way too many sloppy poorly performing applications using Redux to know that it is its own worse enemy. The complexity makes for a terrible result in many developer hands.

I find this comment very interesting as I would have said the exact opposite. I have found Rx gets incredibly complicated and opaque incredibly fast. Some of the most nightmarish code I've ever worked with was due to over use and abuse of Rx.

I'm certainly open to seeing if Rx can be used in a nice way. But so far my experience has been very negative.

But with that said, I also feel you can name any state management system (any one at all) and you will find people who love it, and people who loathe it. I feel in general that state management is in many ways an unsolved problem.

Huge react app veteran lead syatem designer here.

Ended up not using redux after our previous project failing to scale development-wise (it gets harder to put feature in it). Using unstated, it freed developers from god object paradigm introduced by redux's single global connect() function.

Then we did the same, breaking apart states and putting them in the proper scope.

Also, we also removed state from inter-component operations which doesn't really need state, the ones developer usually workaround using global redux states. We replaced it with custom made event sourcing / message passing library.

Think of it as using rabbitMQ but it is running inside your javascript app.

Things get so much simpler after that. Features can be put it a lot faster. Combined with io-ts, it also support almost 100% TypeScript type checks both on compile-time and run-time.

No blank page issue ever since even without a single React ErrorBoundary and minimum unit-test.

I can fully relate to this. Same experience here.

I too am liking RxJS more and more, especially for simple state. I gave up on learning React a few years ago because Redux was just too bonkers.

Perhaps you’re using it wrong. It’s tiny. All you do is manipulate a JavaScript object that holds some state that different parts of your app can access. It’s not much more complicated than that.

2.4 kB minified isn't "tiny" but sure, blame the user.

I was under the impression redux is basically a message bus plus some sugar. Is that wrong?

No you’re right. I find the comments deriding Redux as some bloated beast to be amusing...it’s just a thing that lets you read and update a plain javascript object that different parts of your app can access.

People making things more complicated than they are is really a disease in the dev community.

> it’s just a thing that lets you read and update a plain javascript object that different parts of your app can access

Doesn't require a library, let alone an ecosystem of libraries.

Why implement this functionality from scratch when a tried and tested implementation exists.

To eliminate a dependency.

So implementing everything from scratch has more benefits than a well maintained and documented dependency?

If it's simple, yeah. A redux-type store does not have to be complicated. there are certainly performance optimizations to using redux versus rolling your own, but that would only happen in very large applications.

I agree that you can roll your own store. You'll miss lot of free functionality and the dev tools, but maybe it's not stuff you need.

I also agree that introducing a dependency at every opportunity comes with serious drawbacks.

Every situation is unique of course. In my case: At work, I value code that is more standardised so that I get less bugs, documentation / best patterns and crucially easier on-boarding process. For a personal project, I would definitely consider rolling out my own implementation, even just for the learning it comes with.

Yes, I agree. There are trade-offs to be considered. Those are definitely benefits to using Redux, for sure.

The trade off makes no sense

I am kind of confused, because I view HN as a strong community in terms of development skill but the comments here on Redux are really surprising..

That’s one way to think of it.

The op of this thread is not wrong but the misuse has more to do with the level of discipline the average JavaScript developer employs than anything.

With great power comes great responsibility!

I subscribe to a different philosophy. The whole point of an abstraction is to encourage good behavior. This is how abstractions are able to be leveraged across large teams and organizations to good effect. Abstractions that do the opposite are, in fact, to blame.

"Good interfaces are easy to use properly, and hard to use improperly" should be the bread-and-butter mantra of any library author, and Redux violates this repeatedly.

Something I've wondered about the "immutable updates" pattern discussed in the article: do JavaScript engines optimize it somehow? On its face, it seems like it would turn a constant-time "favorite post X" operation into a linear-time "create a copy of the array of all the posts, replacing post X with a copy of post X where 'favorited' is true" operation.

Or do developers just assume that their UI code won't ever need to scale to large numbers of items? (Is this why JIRA's web UI is so slow when you have a couple thousand open tickets?)

Duplicating arrays of objects is well-trod territory as far as the engine goes. Your array isn't storing all the content, just a bunch of pointers to the old objects, so "a couple thousand" entries in the array is trivially small. Once you're dealing with millions you may consider using immutable.js, since that uses datastructures optimized on the assumption of immutability (eg less duplicating of whole arrays, more storing only the deltas).

If you're using jira's web UI as an example, you'll find the overhead of array and object duplicating to be a tiny fraction of the cost for actually rendering all those items in browser. Probably in the one-billionth or less range of the total cost.

We hit that limit. And yes developers think things will just work - until they don't. When item count and especially app complexity go up, redux becomes cumbersome and you then need to start using action batching, avoid immutability and so on...

Second issue is that developers think they know how Redux works, but there are a lot of gotchas. From example on OP - `return state.map(toggleTodo(action));` With large app, there are for sure cases where such arrays are empty - and this thing will get updated and re-rendered even if it's empty.

From my experience - in most cases MobX is much better than Redux and it's horizontally scalable - as you can just create new, unrelated stores (something that's total PITA with Redux).

Most functional languages have immutable persistent data structures that don't require a linear copy and are fairly efficient.

For example in clojure, sequences are implemented as 32-ary tries so insert/delete/update to any particular index will only cost around sizeOfNode * numParents=32 * log_32(n). This comes with a cost that indexing is now log_32(n) but it's usually a fair price to pay.

In javascript, immutable.js implement these same ideas (as least according to these talks where they talk about index tries and hash array mapped tries: https://youtu.be/I7IdS-PbEgI?t=456)

If you want native support, javascript engines are actually free to swap out their implementation of arrays! I know they already do depending on whether they think your array is sparse("holey") or not: https://github.com/v8/v8/blob/8.1.81/src/builtins/builtins-a.... I also vaguely recall someone saying that unshift (push to front) is automatically optimized by switching to a deque/circular buffer but I might be remembering wrong. Anyway, they can make these optimizations in a distant future but I don't think do today.

A great resource for understanding how persistent data structures are implemented is Okasaki’s Purely Functional Data Structures: https://www.amazon.com/Purely-Functional-Data-Structures-Oka...

Also available as a free PDF: https://www.cs.cmu.edu/~rwh/theses/okasaki.pdf

> Most functional languages have immutable persistent data structures that don't require a linear copy and are fairly efficient.

This is true, but unfortunately those libraries/languages don't make use of that tree structure for "partial memoization" of derived data. They very well could, but they don't -- even when you use Immutable.js in a Redux app, and you use Reselect, your selectors will take linear time when you mutate one element in an object/array instead of taking constant- or log-time.

Yea that's a good point. This matters when your derived data takes linear time to recompute. There are still techniques to make this work (but not automatically afaik). For example it's easy to make them efficient again if your computation is just an associative fold or reversible fold: https://sci-hub.tw/https://link.springer.com/chapter/10.1007...

That article talks about map and filter and fold, but I think there are also (trickier) ways to make joins and sorts work as well.

I think it could be a great technique for a lot of things -- compilers and database views, in addition to front-end state.

immutableJS gives you false sense of immutability - never forget that you have intrinsic reflection in Javascript and the only thing to force immutability is Object.freeze() which makes things unusably slow.

Or you can enforce it with readonlys in TypeScript. Not enforced by the engine of course, so there's always the danger some 3rd party component could get its fingers in your objects, but any of the devs on your project will be prevented from modifying their objects directly.

TS and immutablejs work together surprisingly well. One enforces immutability via static analysis, and the other creates (relatively) efficient datastructures predicated on the assumption of immutability.

TS works well. But ImmutableJS is slow, really slow. https://jsperf.com/immutable-js-iteration

I wouldn't go by benchmarks run against a 100-length array of numbers. That's so small it's practically a tuple. Where immutable.js improves your perf is if you need to have very large (1M+) datasets, and/or need to keep references to those data over time for thing like undo, diffing, saving those diffs, etc. If you used a pure array that's 1M, that'd be over 64MB per array, so at just 10 levels of undo you could already exhaust your heap. Immutable.js would be barely over 64MB for hundreds of levels of undo, since it can rely on the last state being immutable it can store only the diffs.

Immutable.js was a bad solution when it was used purely to enforce immutability. Unfortunately that's what its reputation became, but really it should be thought of as a lib that takes advantage of immutable data, not a lib that checks for immutability.

> Or do developers just assume that their UI code won't ever need to scale to large numbers of items?

If you have enough items that a copy of all attributes is noticeable to the user, the user will never realistically see those items. (We'd be talking about thousands of them minimum) Or alternatively you're loading A LOT of data per item. Either way, the issue in almost every single case is not that the copy takes too long.

I mena, there are edge cases like an endless list of small items (twitter?) which someone scrolls for a long time and then makes a modification. But then you should know it's potentially a REALLY long list you need to handle.

There are definitely costs to doing immutable updates of objects and arrays. The engine has to allocate a new object in memory, copy over all the existing keys, and copy any new keys / values you're assigning. For very large objects, yes, this can become noticeable.

But, for most apps, this isn't a concern given the kinds of data and state updates that are common.

There's a specific library called Immutable.js, which implements some complex internal data structures to optimize copying objects. However, the library is relatively large, and complicated some common usage patterns. My opinion is that it's rarely worth using that lib.

Redux is not that hard to learn so why not?

Why spend more time debating whether to learn something than just learn it?

I'd argue that Redux is so downright trivial to learn, there's a good chance that you already know it. The part that takes a bit of study is learning flux. You should already know how a reducer function works from Arrays, and all that makes the Redux approach special is your accumulator is read-only.

The implications and advantages of all this are huge and take some time to really appreciate, but you shouldn't lose much time being confused and unproductive. The flux concepts of one-way data flow, action creators, etc. are the bigger ideas that take some study.

It's hard enough. I haven't needed it in developing/contributing to my own frameworks and there's an infinite amount of things to look into. Right from the Redux docs (https://redux.js.org/faq/general/#when-should-i-use-redux) - the link https://gedd.ski/post/what-not-to-learn/

Dunno. Perhaps there’s more functionality than the core but all it is is a plain JavaScript object that you “update” that you can access from different parts of your app.

It really isn’t that complicated.

My brain I guess works in a different way. I could never truly understand/appreciate value of the Redux for state management (well except that it is better then nothing).

For this type of things I've always used one or the other implementation of State Machine pattern. Seems way more clear to me.

It is not just about JavaScript. State management is a part of many applications and State Machine is a nice paradigm to work with. At least for my taste.

The key values of Redux are separating state update logic from the rest of the app, emphasizing writing as much logic as possible as pure functions that are easy to test, and enabling developers to better understand when, where, why, and how their state is changing over time.

Note that it's entirely possible to write your reducer logic as state machines, and in fact we explicitly encourage that :


In my opinion State Machine is sufficient on it's own. To me reducers seem superfluous and in fact distracting from actually managing state. But as already said it is my own opinion coming from my own experience. We do not need to convince each other

I honestly have no clue about Redux. I know a bit about react and used Mobx but I’m not a huge fan. When it comes to state managers and especially bigger frameworks with best practice patterns I get reminded a lot about PureMVC. This was the framework in the late 2000th that claimed to solve everything because it’s so simple and slim. But with a bigger application the whole boilerplate setup exploded. And don’t get me started with PureMVC pipes etc. Reading the comments here sounds like Redux has kind of the same issues.

What about the built-in Context api ?

Absolutely. For most projects it is more than enough. If you have a basic CRUD app with token authentication you can get away with using that. There has been better form support thanks to new libraries coming out recently so I don't see reason to learn redux is, as was pointed out by other commenters, very verbose and requires a lot of boilerplate code to get started.

The context API is baked in, no external lib required. If you can afford to update your react codebase to the latest version without major breaking changes I would totally migrate to the context API.

Context is a useful tool, but its only purpose is to make value accessible to deeply nested components. It's not even a state management tool - you have to manage whatever state you want to put into that context, by yourself.

The useState and useReducer hooks start to resemble Redux a bit more when used together with context, but they definitely do not replace Redux.

For more details, see these posts :

- https://daveceddia.com/context-api-vs-redux/

- https://www.simplethread.com/cant-replace-redux-with-hooks/

- https://staleclosures.dev/from-redux-to-hooks-case-study/

- https://medium.com/javascript-scene/do-react-hooks-replace-r...

- https://dev.to/chrisachard/can-you-replace-redux-with-react-...

Redux = functional zealotry

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact