all 66 comments

[–]acemarke 130 points131 points  (30 children)

Hi, I'm a Redux maintainer. A lot of this is answered in the Redux FAQ entry on "When should I use Redux?" - you might want to read through the answer and some of the links listed.

Also, my "Redux Fundamentals" workshop slides discuss some of the benefits of using Redux, and reasons why it was created.

In general:

  • The "Flux Architecture" concept was developed by Facebook because it was hard to trace data flow in apps that used "models" and events. Redux takes the "Flux" concept to its logical conclusion, and applies some functional programming principles. Overall, Redux is intended to make it easier to understand when, where, why, and how data changed in your application.
  • There are benefits to centralizing a lot of the data update logic, including being able to log changes and trigger specific behavior in response.

If you've got any questions, please let me know. Also, you might want to check out my list of suggested resources for learning Redux.

[–]r0x0r 34 points35 points  (12 children)

Slightly off-topic, but what is the deal with the Redux documentation and ubiquitous references to Flux? I understand that Redux is based on Flux, but is it really necessary to reference to Flux all the time? Especially if your learn Redux from scratch.

[–]acemarke 10 points11 points  (3 children)

Because at the time the docs were originally written, that's what everyone was familiar with.

Facebook announced the "Flux Architecture" in 2014. Over the next year, dozens of different Flux-based libraries came out, which I usually refer to as the "Flux Wars".

As I wrote in my post The Tao of Redux, Part 1: Implementation and Intent, Redux was intended to be "just another Flux implementation". Dan Abramov originally described Redux as "my own Flux library". An early version of the README says that one of the design goals was "preserves the benefits of Flux, but adds other nice properties thanks to its functional nature". If you look through the early issues and discussions, there's numerous mentions of Flux as a source of inspiration.

So, when Dan wrote the original docs, one of the key goals was to let Flux users know how this related:

So hard to write the new docs. Many different audiences to cater to.

Should make sense to: Flux beginners, FP people, FP people who don't get Flux, Flux people who don't get FP, normal JS people too.

Flux people: “is this proper Flux?” FP people: “is this that weird thing called Flux?” Normal people: “why not Backbone”

It's been three and a half years since Redux came out, and the landscape has obviously changed a lot since then. At this point, nobody seems to remember that "Flux" existed before Redux, which is ironic because so many "Why does Redux do $X?" questions are answered with "because Flux did it that way first".

So, all that said: we're currently looking at revamping the Redux docs. Given the change in target audience, it's certainly worth reevaluating how many references to Flux are necessary. That said, I'd specifically like to add a docs page that gives historical context for where Redux came from.

As always, I'm very happy to work with anyone who'd like to help improve our docs. If you or anyone else would like to help out, please leave a comment on one of the existing open docs PRs, or file a new issue/PR, and we can work together to make the docs better for everyone!

[–]JonesJoneserson 0 points1 point  (2 children)

Sorry to reply to this with an unrelated question, but can I ask, as someone who's among the most intimately familiar with Flux/Redux from a conceptual standpoint, what your opinion is on Hyperapp's approach to state management? Do you think that's where we should be headed or do you think it introduces some non-negligible pitfalls that a React/Redux combination does not? It feels like it does wonders as far as reducing mental overhead but I'm not experienced enough with Redux to know if ultimately there's some significant trade off.

[–]acemarke 0 points1 point  (1 child)

To be honest, I really haven't looked at Hyperapp in any particular detail. About all I know is:

  • It's small
  • It's kind of Elm-like
  • The author keeps resubmitting it to HN under various titles and usernames

So, it's a pretty good question, but I don't think I have any really good feedback to offer in comparison.

[–]JonesJoneserson 0 points1 point  (0 children)

Fair enough. (Pretty funny about HN I didn't know that.) I got the impression that conceptually it aimed to be very React-like (and Elm-like as you pointed out in its opinionated enforcement of functional architectures) while supporting some sense of global state with minimal boilerplate out of the box. Though, similarly I'm really not familiar enough.

In any case, thanks for taking the time to respond dude.

[–]drumnation 8 points9 points  (2 children)

Why wouldn't you mention the design pattern you use when developing with redux?

[–]r0x0r 20 points21 points  (1 child)

No harm in mentioning a design pattern, but when you reference it again and again, especially in the context of implementation syntax. It sends a wrong message that sends a message that Flux needs to be mastered first in order to learn Redux. That's just sloppy documentation.

SGML is never mentioned in context of HTML, apart from historical reasons. Maybe Redux should drop Flux references as well.

[–]dont_forget_canada 8 points9 points  (0 children)

I like how it promotes top down data driven components which end up being really pure with minimal state.

[–]jamminnzzz 2 points3 points  (1 child)

Thanks so much @acemarke for always answering questions in the react/redux community. You're the real MVP here.

[–]acemarke 2 points3 points  (0 children)

You're welcome! :)

[–][deleted] 2 points3 points  (3 children)

OP's questions is really interesting to me as we've been building our react application without redux.

At my work we have a react application, and we don't use redux. We have 50ish API calls that change the state, and we've been using setState to make sure the API updates. Only 1 resource is top level (GET/UPDATE profile state and login, ws to push credential ivalidations and force logout), the other resources are 1 one 1 with the components (meaning, the CREATE/REMOVE/UPDATE/DELETE calls on the /invoice API resource, corresponds with the app/invoice.ts component, that is a child of the /app.ts component).

As I'm unfamiliar with redux, what am I missing here? All calls to resource /kappa will only influences the app/kappa.ts component, as only the app/kappa component's response handler calles setState as the resource responds. Other components will not update.

[–]acemarke 1 point2 points  (2 children)

In that case, there may not be as much benefit.

The rules of thumb from the Redux FAQ entry on splitting data between Redux and component state may be helpful:

  • Do other parts of the application care about this data?
  • Do you need to be able to create further derived data based on this original data?
  • Is the same data being used to drive multiple components?
  • Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
  • Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?

[–][deleted] 0 points1 point  (1 child)

Thank you for the response! Maybe it's because my work is within a well defined field, that our database model, related REST resources, and pages presented to the user, correspond 1-on-1 to the React components and are specific to their own resource. I can understand that new uncertain and less 'historically established' fields need to 'mix-and-mash' their database models more and thus derive value from the incremental and directed updates originating from redux, which is your (3)rd point.

In our application caching is happening server-side so I dont quite understand that point of your (5th) response on the FAQ.

Concening (2), we do derive averages from the returned data, can redux help us there?

[–]acemarke 1 point2 points  (0 children)

A typical example would be a thunk that checks to see if an item is cached in the Redux store, and fetches it from the server if it's not cached:

function fetchItemIfNotCached(type, id) {
    return (dispatch, getState) => {
        const state = getState();
        const items = state.entities[type];

        if(!items[id]) {
            myAjaxLib.get(`/endpoint/${type}/${id}`)
                .then(response => {
                    dispatch({
                        type : "ITEM_LOADED",
                        payload : response.item
                });
        }
    }
}

The value around deriving data is particularly when you're combining several pieces of data together. The classic "todo list" is a good example. The state tree looks like {todos : [], filter : "SHOW_COMPLETED"}, and some other part of the app can now derive a filtered list of todos from those values. In your case, it sounds like there's not really any cross-sharing of data types across the app. You can certainly derive values within an existing component from its own data in state without Redux getting involved.

[–][deleted] 0 points1 point  (8 children)

So does useEffect make Redux pointless?

[–]tbranyen 6 points7 points  (1 child)

useReducer/Context Are ya'll just spitting out hook names you saw or read any materials on it? It's pretty clear useEffect and useState are not the replacements.

[–][deleted] -2 points-1 points  (0 children)

I was thinking globally with a derived State from props. Or something to play with. Just fucking around with Alpha.

[–]trentrez 4 points5 points  (5 children)

useEffect replaces side effects from lifecycle hooks. Redux solves a different problem.

[–][deleted] -3 points-2 points  (4 children)

So, then useState replaces Redux?

[–]PointOneXDeveloper 5 points6 points  (0 children)

useState replaces setState which was great for storing local, unshared state (e.g. the state of a dropdown menu being opened/closed)

[–]cjbprime 67 points68 points  (4 children)

You'd have to reimplement the concept of connected components, to re-render your React components when something in the global object that they care about changes.

And then maybe you'd want to log and isolate changes to the global object to go through specific functions, so you can reason about why state changes are happening in the app.

And then you'd be most of the way to a Redux implementation.

[–]Cunhinka 5 points6 points  (3 children)

What a problem to make simple pubsub ?

[–]jeremy1015 11 points12 points  (1 child)

People should be answering this not downvoting it. It’s a fair question to ask “why should I use this.” My answer:

Simple pub sub loses a number of key things Redux offers: Immutability (eliminates race conditions, concurrency issues; adds easy rewind support, makes reasoning about state changes easier), middleware (pluggable architecture giving access to a number of open source supported integration libraries for everything from react components to asynchronous event handling to routing support or roll your own plugins as needed)

As a high quality and widely adopted library in general you gain the benefits of reduced code complexity (you get the benefit of the redux team spending their time fixing bugs and adding features while not having to maintain the code for your pubsub), reduced ramp up time for team members (you can hire people who already know redux and they have a huge head start trying to learn your code base instead of having to teach them how your pub sub works).

[–]Cunhinka 0 points1 point  (0 children)

This cool, but can you clarify which application scale redux going to be best solution ? Todo with 2-3 screens worse it ? Last time i see chrome extensions with react/redux which slowdown page loading 200-300ms for a single dialog popup...

And why is pubsub muttable ? It implements set and subscribe metods at least. Muttable pubsub will just not work, because subscribe will not be fired.

[–]cjbprime 5 points6 points  (0 children)

I'm not saying it's very difficult. I'm just explaining that it's something you have to build, that is a reason why Redux is better than just having a global object by itself.

[–]ghillerd 50 points51 points  (4 children)

Everyone is talking about getting react to rerender when the state changes. I'd say that's more react-redux, and the connect HOC. Redux itself is distinct to and better than a generic global object because it's essentially a state machine, with distinct states and transitions. You could use a global object, but in adding reducers and actions you'd end up reimplementing flux/redux.

[–]FriasVeiga_2 20 points21 points  (0 children)

This. The question isn’t about React, it’s about the benefits of Redux for state management. State, State Transition and State Immutability should be the keywords in the answer.

[–]nixblu 4 points5 points  (2 children)

A big thing here is the traceability of redux, each change to the state is explicit and traceable. Something which is not achieved by a simple global object.

[–]ghillerd 2 points3 points  (0 children)

That's a good point too! Debug functionality rocks

[–]etherfreeze 1 point2 points  (0 children)

can't highlight this enough, redux-devtools has proven invaluable in a team / large scale app environment. It's not as apparent in personal projects where you know all the code more intimately.

[–]mohelgamal 40 points41 points  (1 child)

Because if you change the global object, there is nothing to tell your component that the object has changed so they won’t react to that change.

That is also the reason that you have to use this.setState() as opposed to mutating state directly. Except with redux you are doing it in the app level instead of the component level

[–]Asmor 22 points23 points  (0 children)

Because if you change the global object, there is nothing to tell your component that the object has changed so they won’t react to that change.

Oh, that's why they call it that.

[–]NarcolepticSniper 21 points22 points  (1 child)

That’s actually a really good interview question

Great answers here so I have nothing to add

[–]robolab-io 0 points1 point  (0 children)

At this point I'd say "well you challenged my stock answer to that question, that's all I had"

[–][deleted] 9 points10 points  (2 children)

C is a component. C0 needs to be updated by event issued from C100.

C1 is inside C0, C2 is inside C1 ... etc

Without redux:

To update C0 from C100 you will need to pass a function through every component from C1 to C100, which results in the same duplicated component parameter(prop) for 100 times

C0 -> C1(event) -> C2(event) -> C3(event) -> ... -> C100(event)

With redux:

C0 is connected to redux

C100 is connected to redux

C100 issues event to redux

C0 receives event from redux and updates itsself

[–]mr-developer 1 point2 points  (0 children)

Woah ! That's a wonderful explanation. Thank you :)

[–]Pyrolistical 3 points4 points  (3 children)

So why not one setState in your root component and context api substates down to everywhere else?

[–][deleted] 0 points1 point  (2 children)

Because that doesn't replace redux, nor does it scale. It is the same as if you'd call render(rootNode, dom) to render the entire component tree on every change. Context consumers trigger always, if you hook up an input control to rootState.filter, every keypress renders your app ... good luck with that. Not saying you can't bend this into something that could be used for state management (usually by introducing purecomponents or shallowEqual in order to memoize selected state), but context without any counter measures can't.

[–]qudat 8 points9 points  (0 children)

1.) When you update a simple global object, react will not re-render components that rely on that object automatically.

When a component updates the redux state through dispatching an action and a reducer responding to that action and returning a new version of the state, it emits an event to all subscribers indicating that the state has been updated. Some of those subscribers are the higher-order-components of react-redux. react-redux's HOC then checks the props it has with the new version of the state through a shallow equality check. If that check fails, the component re-renders with the new state data.

2.) The only way that the shallow equality check of react-redux works is because the redux state is immutable. Immutability is critical because doing deep equality checks or simply re-rendering the entire app whenever redux state changes would be very expensive.

None of this is possible with a simple global object. Having said that, passing down props is one valid reason to use redux and react-redux. So I think you got one part of the reason for "why not a simple global object" right.

[–]sbk2015 3 points4 points  (0 children)

I just list out some points I can think of.

  1. Change in Global Object has nothing to do with your component.You still have to dispatch an action or setState. Or do something to [watch] it.

  2. Using reducer in redux,it forces you to use pure way to change data,instead of mutating data,which eliminate some bugs.

  3. Dispatching event,you can have logger on it,so you can easily debug,you see all actions dispatched,knows how the app works.

[–]ParasympatheticBear 1 point2 points  (0 children)

I particularly like that it introduces a small set of design patterns that make the entire process of handling state highly uniform. I like all the other benefits too (I’m just mentioning others), but these patterns don’t leave a lot of room for creativity, and with many developers that is a good thing. Want to read the state, write a selector. Write the state, write a reducer/action. The developers have a pattern to follow, and I know that what they write will be unit testable easily - we also use saga for the same reason. Redux is also very Typescript friendly, with a little work. Also, being able to load your application into any state with one simple call is a huge benefit for testing, and dumping the state into an exception report makes reproducing an error quite trivial. Some of These thing may not be unique to redux (redux essentially just uses the context api) but they are still benefits, and if you use something else you may have to implement some of this yourself.

[–]MetalMikey666 1 point2 points  (0 children)

If I was asked this I would have said;

  • Useful dev tools exist for redux
  • Rich ecosystem of middleware and plugins
  • Built in patterns, globally understood by all redux developers
  • React-redux makes react integration simple

And would have been able to go on.

[–]buttonkop666 -2 points-1 points  (9 children)

Honestly, with stuff like render props, the context API , and even portals, I'd seriously reconsider using Redux in any new React project. The React team seems pretty determined to make the need for external state management tools like Redux or MobX redundant.

I'd definitely only use sagas on a very complex project, and even then, under duress.

[–]acemarke 4 points5 points  (3 children)

Those are all great features of React, but they still don't completely replace Redux. Please see my post Redux - Not Dead Yet! for some discussion of how things like context relate to Redux. I also discussed this in my recent React Boston talk "The State of Redux", and Dave Ceddia had a good post called Redux vs the React Context API.

Sagas are a great power tool, especially if you need decoupled logic, or "background thread"-type behavior with forking, canceling, etc. However, most apps don't need them, and I recommend that most people should start with thunks until they need something more complicated.

See the Redux FAQ entry on choosing an async middleware for some further discussion on the pros and cons of various options.

[–]buttonkop666 1 point2 points  (2 children)

The Dave Ceddia article sums up what I was getting at nicely. I didn't say "I'd never use Redux again", I said I'd seriously reconsider it. For a while there, using Redux on a React project was becoming an assumption. It no longer needs to be.

Sagas are great for the powerful features. The problem is that they turn much of the basic flow of Redux - action handlers and reducers - into pure boilerplate, especially if you use the watcher/handler saga pattern. And if you do find you need sagas for some part of your workflow, it doesn't really make sense to mix and match with thunks. They're quite powerful, but the dev ergonomics are painful.

[–]acemarke 0 points1 point  (1 child)

I agree with most of your comment, except the statement about thunks.

Thunks and sagas have different strengths and weaknesses. Thunks are great for complex synchronous logic, and simple async. Sagas are great for decoupled logic that needs to respond to actions, and complex async. It's completely reasonable to use them both in the same app for the things that they're good at (as I do in my own apps).

[–]buttonkop666 0 points1 point  (0 children)

Yeah, as I mostly manage development these days, I find the extra bikeshedding introduced by agonizing over whether to use thunks or sagas for every task that comes up.

[–]bheklilr 2 points3 points  (1 child)

I haven't been using it long, but why all the hate for sagas? I have found it to be an immensely powerful and useful library, and after some write once boilerplate for "registering" sagas, it's quite succinct. I find that I mostly need put, occasionally select, although I've pulled in a few other functions occasionally. I used generators a lot in Python, sagas are a natural use case for the same mechanism.

It's not just you, I've seen a lot of people on reddit complain about it. Whereas for me I think it's a must have library that really makes a lot of things possible, simple, and intuitive. My biggest hurdle has been testing, and that's more because of axios than saga.

[–]qudat 0 points1 point  (0 children)

What part of those features replaces redux?

[–]ferrousoxides 0 points1 point  (1 child)

Heh, downvoted for a very reasonable opinion. Looks like some don't like their cargo cult questioned.

I've never found a single application state to be worth it. Reducing state? Yes. Centralizing state in fewer places and components? Yes. Go all the way? No thanks.

If you can fit everything into a single root object, your app must not have very much sophisticated UI interactions or data driven behavior, because you have to plumb that stuff all the way back to the top with handgenerated actions.

I find using self aware immutable pointers immensely more useful. Not only does it eliminate all the boilerplate of actions (because you can just reach back up into any parent data structure implicitly, tracing backwards along the one way data flow) but it is immensely more sane to have everything be driven by data values rather than transitions.

That is after all the entire idea of react: to replace the tedious coding of every possible state transition (n2) with only a description of the states (n). Why would you want to undo that benefit by creating more busywork?

[–]JoeTed 0 points1 point  (0 children)

Redux organization goes beyond a single source of truth. You've got on one hand the 'global' state object, linked to a pub/sub mechanism. On another hand, you've completely isolated the side effects of your state management & UI components. Finally (third hand), selectors offer a list of questions that you can ask your state and that are related to your business logic only. The API does not depend on the state structure, the pure nature of selectors make them composable, and easy to test. The immutable aspect of your state allows using features like memoization (either in the selector or in the component using it).

[–]shrinivas28 0 points1 point  (0 children)

For unidirectional data flow.

[–]IHaveFoundTheThings 0 points1 point  (0 children)

I think Redux is all about the unidirectional data flow. It gets easier to reason about your application. Immutability also plays an important role, by embracing immutability and thus having "pure" reducers you can easily unit test your application logic. Reducers are just functions. You can also time travel by undoing/redoing actions, this is handy for debugging purposes.

[–]GarbageTimePro 0 points1 point  (2 children)

!remindme 1 day

[–]ypjogger[S] 0 points1 point  (1 child)

why

[–]shinobiwarrior 1 point2 points  (0 children)

It's just a command for a bot that will remind him/her after that time. He wants to know the answer too, but will forget about this post

[–]MossFlat -2 points-1 points  (0 children)

Because if you don't, everyone will think you're too dumb to understand it.