Hacker News new | comments | show | ask | jobs | submit login
Redux vs. The React Context API (daveceddia.com)
207 points by wheresvic1 32 days ago | hide | past | web | favorite | 73 comments



React Redux actually uses the Context API (Update: Old context api).

"Context is what powers React Redux under the hood. The new context API isn’t to “replace Redux”, it just fixes a broken feature that needs to be provided by React. Whether you use it directly or through another API like RR is up to you" [0]

At the end of the linked article the author states "Today, when Redux is more than you need, you can reach for Context." I'd actually argue the opposite. Redux, specifically React-Redux, is an abstraction. An abstraction that simplifies using the Context API. All I need to think about when using Redux is Flux architecture. There is excellent documentation around Redux, context api not as much.

If you don't feel confident about Redux, or would like to get a better understanding of it, check out these three things (in order):

1. Understand Flux Architecture: https://facebook.github.io/flux/docs/in-depth-overview.html#...

2. Understand Redux: https://egghead.io/courses/getting-started-with-redux

3. Implement Redux: Read the documentation at https://redux.js.org/. At this point you should have a good grasp on the concepts so pay attention to implementation details here like designing the state.

[0]:https://twitter.com/dan_abramov/status/964946781493780480?la...


We use the legacy Context API: https://github.com/reduxjs/react-redux/blob/47facdbcae2412d3...

This article is looking at the new Context API, which uses React.createContext() to create a Provider/Consumer pair of plain components. This has a number of differences in behavior, most critically around blocking updates incorrectly.

We are looking to switch to the newer API in 6.0. We have two in-progress PRs for this:

https://github.com/reduxjs/react-redux/pull/1000

https://github.com/reduxjs/react-redux/pull/995


Very cool, thanks for the clarification.

To me, this is yet another perk of using RR on top of the actual api. As someone who works on a smaller team, updating our projects to use the new context api would be a significant undertaking. By using RR, the implementation details are left better equipped engineers, our team can focus on meeting business requirements.


Yep, and that was always the advice from the React team. "Don't use legacy context directly in your apps. Libraries may need to use it. If you _need_ to use it, wrap it up in a HOC or something so that you can update it in one place later."


Redux isn’t a simplification of React context; it’s solving a different problem.

Context is the mechanism by which state is passed through to components. Redux solves management and lifecycle of that state as well.


There’s a bunch of useless event boilerplate in redux. Worse, it seems to have been conceived without any thought for asynchronous operations (like making requests) and this leads to bizarre patterns like redux-thunk, and even the monstrous redux-saga.

I made my own state management framework with context, but it’s basically a less polished version of unstated if you want to check that out. Much saner. Much less cruft.


Redux contains a number of different ideas/solutions, and I think it's maybe more fair to view them independently. Redux solves prop drilling via the connect function (technically in react-redux, not core redux), and you might say that Context competes with that aspect. Other aspects of Redux are completely unrelated to Context and also good to understand: a unified immutable state tree transformed via composable pure functions in response to a well-defined set of actions represented as objects. Not that you can't do that with Context, but I think it's maybe unfair to view Context as a replacement for Redux.


> I think it's maybe unfair to view Context as a replacement for Redux.

That pretty much sums up why I wrote this article :) After the new Context API came out, a lot of people started saying that it would replace Redux, and like you said, they only really overlap in that they can help avoid prop drilling. Redux is more powerful, out of the box anyway.


Definitely, the power of redux middleware should not be underestimated, e.g. redux-throttle[0], redux-optimistic-ui[1], redux-persist[2]

[0]: https://github.com/mathieudutour/redux-throttle

[1]: https://github.com/mattkrick/redux-optimistic-ui

[2]: https://github.com/rt2zz/redux-persist

(to be pedantic, some of these are not solely middleware)


Funny, I vastly prefer the simplicity of passing down props over HOC spagetti - even when heavily nested. It is a simple matter to keep things organized and out of your way, and change detection is taken care of by selectors.

I used to think props were good for dynamic components, but with portals available, I rarelyconsider them a good pattern for anything but components shared between applications.

I use context to pass down actions. IMHO, mapping those into state should be deprecated.


I have a very different point of view: HOC are core to my way of doing things, as I try to make each component as self-contained and easy to use (fewer props) as possible. Why would I give a user object to my component when it could get it by itself?

It means that components are more coupled to the state, but way way less coupled between them. Which I find to be easier to work with!


So why use redux at all then? Why not just subscribe to a bunch of selectors?

The last codebase I saw that was written with an HOCs-first perspective was rendering every HOC on every prop trigger. It still gives me the twitches :)


> So why use redux at all then? Why not just subscribe to a bunch of selectors?

Not exactly sure what you mean by that? Selectors are a redux thing for me, I don't understand what you mean by not using redux but subscribing to selectors

> The last codebase I saw that was written with an HOCs-first perspective was rendering every HOC on every prop trigger. It still gives me the twitches :)

I don't see how that's a problem. Just because you render a bunch of wrapper components (one for each HOC)? React is extremely efficient at rendering this kind of components having only one child


Not sure I follow. Selectors don't need redux. I use them everywhere.

https://github.com/reduxjs/reselect

> Just because you render a bunch of wrapper components (one for each HOC)?

Efficient rendering would involve >not< re-rendering the child on every prop render, but only when the relevant props have changed. Connect does a shallow comparison, so all the props need to be immutable for it to work.

You can use immutablejs to make your object props immutable, but this gets messy fast. I have found it easier to use selectors, and have changes land when an actual change happens, rather than checking for them or organizing your data to facilitate change detection (rather than organizing your data to make it easier to think about).


There’s a risk that people will start using context everywhere as a minor convenience. This is the reason the React devs kept the original API so obscure.

I try and limit context to the things that are used across the app, but are not coupled to a given component. For example the user’s session, environment specific behaviour, or theme information.


I just use it for actions, I have to confess :)

Most of the time I feel that global-ish data like that shouldn't be passed/exist in the first place. Context is a doubly dangerous place for it, since it can go stale.

I take that back. I'm am accessing this kind of data - via the actions I'm passing down. They are `getStore`ing anything they need.

Anything else that needs to have the data >should< access via props, or the redux store is no longer the single point of truth.

    Footguns, footguns, everywhere. 
    And us, with four left feet.


Have you tried MobX? I feel somewhat the same, and I’m that way MobX has been a good in between for my work involving web interfaces. It’s been extremely easy to implement into older projects using no globalized state as well.


We use MobX as a dependency injector, and it’s been great. Scales up very well and is fairly simple to get people up to speed when they drop into our project.

Redux was a “wtf” as it grew in our app and was a real struggle to back out of.


Thanks, I'll check it out.

My current personal project is a video game drum controller trainer that I am writing in Bacon.js streams instead of redux. So far it's been a mindbending experience - I'll have to master it before I know if it's any good :)


My experience seems to be very different. I have used redux extensively for two years and love it. It is well thought out and solved a problem in a brilliant way. I have however, been ripping redux out of all of my projects, one by one and replacing them with the Context API. I remove file after file of actions, reducers et al and replace them with one context Provider.

Someone commented that the power of redux should not be underestimated and that is absolutely true. It is also true that the complexity of redux should not be underestimated. My code bases have been greatly simplified.

In my limited experience the Context API covers 80% of the use cases that required redux, with 80% less complexity. I am sure I will use redux in the future, and I am a better coder for having learned it, but my experience with Context has been very positive.


I’ve had similar experiences. Redux is very elegant in principle but I’ve found that it results in an explosion of boilerplate and hard to follow code paths. By the time you’ve pulled in something like redux-saga to handle async the lovely simplicity that made react so popular in the first place is a distant memory. And the mess that an eager but inexperienced developer can make with this stack is truly something terrible to behold.


This is a good article, but the title makes the wrong comparison:

* Context is a dependency injection utility. * Redux is a state-management library.

Redux doesn't know anything about React. In order to use it with React you need to use ReactRedux, which actually uses Context to do it's thing.

The comparison being drawn with the article is still the old:

> React Redux vs React State.

The difference now is that React has a new (and substantially better) Context API.


Yes, Redux by itself knows nothing about React. But, the vast majority of Redux users are using React, and it's a reasonable shorthand to just say "Redux" in this case. It's also how most end users would tend to refer to things.

FYI, I'm a Redux maintainer. We are currently working on an internal refactoring of React-Redux's `connect` so that it will use the new `React.createContext` API instead of legacy context. We've got two open PRs with "competing" reimplementations, and I will actually be spending the rest of today hacking on those.

The immediate goal is to make React-Redux at least compatible with React's upcoming "async rendering" capabilities. However, we do have some difficult problems to solve in order for Redux to take full advantage of the "Suspense" feature. We've got a decent idea what the problem space is, but we need some more examples and guidance from the React team so we can try to work on a solution.

Side note: we're also hoping to completely rework the React-Redux docs (since they're mostly just a jargonistic API reference in a Markdown file in the repo), and we're also considering a restructuring / reworking of the core Redux docs as well. Would love to have some suggestions or even additional volunteers to help us!

Issues/links for reference:

- Roadmap: https://github.com/reduxjs/react-redux/issues/950

- v6 WIP PRs: https://github.com/reduxjs/react-redux/pull/995 , https://github.com/reduxjs/react-redux/pull/1000

- React-Redux docs: https://github.com/reduxjs/react-redux/issues/1001

- Redux core docs: https://github.com/reduxjs/redux/issues/2590


The egghead Redux series has been very popular and useful for users. We’d love to talk about how we can compliment this documentation revamping with updated/new free resources. joel@egghead.io


Yep, it's highlighted in our README and "Learning Resources" docs pages, and it's the first thing I recommend after the docs in my own "Suggested Resources for Learning Redux" blog post ( http://blog.isquaredsoftware.com/2017/12/blogged-answers-lea... ).

Leave a comment in issue 2590, or DM me on Twitter or something - happy for any assistance you can offer!


I live in an Angular world, and a lot of what you’re describing sounds like ideas that are common in many Angular setups, with async rendering of state not completing until a data stream holding state passes through an async pipe in the template/view.

How much conversation is there with the ngrx (AngularRedux implementation with Redux as a leaf-able RxJS stream) folks? This is essentially an async integration, wondering if there is some good additional idea exchange to be had there.


I haven't used RxJS or Angular myself, but my impression is that the React "Suspense" capabilities are pretty novel compared to other JS frameworks. Check out Shawn Wang's "fresh-async-react" repo for a list of resources explaining the concepts: https://github.com/sw-yx/fresh-async-react .

Honestly, the NgRX team is completely separate and we don't talk. I proposed a get-together back in March when I was out in SF for a few days, but it didn't pan out (see the thread before and after this tweet: https://twitter.com/acemarke/status/969035239392776193 ). I'd love to have some discussions to see where we can maybe share some ideas, see where the ecosystems actually overlap, etc.


The ReactReason devs seem to be averse to adding the context API (fears of instability). Does the Redux team have any similar reservations?


I haven't looked at ReactReason beyond seeing it mentioned in a few conference slides.

The new context API (`React.createContext`) is intended to be stable going forward. As far as I know, the only potential "unstable" aspect remaining is the `changedBits` prop for potentially optimizing which consumers actually update on a given context value change. So no, I don't have any particular reservations at the moment. Legacy context still works but is very deprecated, and new context allows us to refactor the internals in a way that's ultimately simpler and hopefully more compatible with where React is going.

Can you point me to some of these ReactReason discussions?


https://github.com/reasonml/reason-react/issues/199#issuecom...

It's one of the big reasons our company isn't even considering ReactReason at present. Lack of support for version 16 APIs is killer when you already have stuff depending on them.


Huh. I'm very surprised to see that comment coming from Cheng Lou. React 16.3 came out in March, and the React team has been encouraging everyone to use the new context API since then. Really not sure what's behind his comment.


> I’m going to assume you’ve got the basics of React down pat (props & state), but if you don’t, take my free 5-day course to learn the basics of React

Gotta love the contextual marketing in a still-useful article. This made me realize content marketing combined with educational tools in an underrated small business idea, which I'm sure could apply to a hundred small niches when done right by legitimate domain experts.


And I can completely vouch for Dave's content, too. I frequently refer beginners to several of his articles to answer their questions. Highly recommended!


Ya. Like a lot of hacker news posts they are really just advertisements.

I bought Dave's book a couple of months ago. It's overpriced in my mind (it's more like half a book and stops short of delivering what I think most people would expect or want, i feel) but he is a good writer and what is in the book is pretty informative and solidly instructional.


Thanks for the honest comments. What would you like to see in addition to what it covers so far?

While I intentionally kept it focused on "pure React" because I felt that a big part of the "React is hard" sentiment seems to come from how much other stuff gets lumped in with it (and then people feel compelled to learn it all at once), I've been considering expanding it to include some other tangential stuff e.g. API calls, basics of testing... any other ideas would be great to hear!


The big hang up I remember having was the desire for a bit more into stubbing out when and where AJAX belongs. I felt like I still had a gap between the dummy application's data handling and how I'd be hitting my server side APIs when I needed them.

I'm a mostly backend programmer and i remember when i was done with the book thinking "ok i can shoe horn my ajax calls into this but I don't feel confident this is how I'm supposed to." ultimately I got set straight on IRC and discord.

I do enjoy your writing style and your blog posts though, and honestly did find your book helpful.


Same here, but I considered part of the purchase price to be a thanks for the many useful blog posts and, as a result, am very happy with my purchase. Thanks, Dave!


Cheers, thank you too!

Pricing is hard :/ Price too low, and people don't take it seriously. (e.g. $9 for 35 hours of video). Price too high and people run away. It was my first product, too. I looked at it as, "how many hours would this save someone?" Filtering through bad tutorials, just-in-time googling, all that stuff. Of course with front end there are a billion other learning resources out there, lots free, lots cheap. Hopefully the book is a more direct path than some of those others. Always room for improvement and experimentation of course :)


Educational and useful content marketing is definitely a solid plan. That's been my strategy all along and it seems to work! I figure, some people might wanna sign up for a thing, or enjoy feeling like they're in a "class" of sorts (with the 5-day thing). But even if they don't want any of that, I still want the post to be useful. Give an insight, help them take action on something, etc.


I really really like mobx-state-tree (which is way different from just mobx). I'm not sure where I'd use the Context API over MST, but I'm currently toying with comparing both. Offhand, MST seems to be friendlier to Typescript.


MST is a beast, I use it for everything. It great for abstraction, has good typescript support. tree based nodes are just a super nice structure for applications in general.

I've used flux, redux, mobx, context, combinations of those, and I think I finally found something that will stay.

If you haven't tried it, I highly recommend it!


Do you use it with React Native?



The typescript annotations for immutable.js do have a lot of limitations. I wound up writing a bunch of my own.


If you just want to share state use context. Maybe use something like statty or unstated to remove some boilerplate code.

Redux is nice if you need more predictable interactions, for example when you need undo.


Context is great to work with and solves most of our usecases. I also recommend looking into Unstated[0], simple api and easy to implement.

[0]https://github.com/jamiebuilds/unstated


I use constate which is very similar to Unstated: https://github.com/diegohaz/constate


I'm not a React dev, but both approaches seem like they would make testing harder. Prop drilling must be the most testable approach. Am I wrong?


It definitely makes testing a bit different. The downside to prop drilling is that you get components with lots of props, and it can be tedious to figure out how to render a component at all if you need to pass in 20 props for all of its transitive dependencies. The upside is that you have a clear list of the 20 needed props, and once you've figured them out, your component will work.

With Redux and Context, you might have test setup code always populate the store with reasonable defaults for the 20 different data dependencies. Then, a typical test might update a few things in the store with test-specific values, render <Nav /> without the need to specify any props, and then perform interactions, inspect the results, etc. There's more going on behind the scenes, but it's also easier to get started because you don't need to figure out 20 props.

(There are many, many ways to think about testing here, though, and this is just one approach.)


That's why most people say to separate the connected component (that gets few props) wrapping the unconnected one (that gets a lot of props). You usually test the unconnected.

If you want to test the connected component, it's still good to separate them as you won't test the same things (correct props are passed down, rather than UI renders)


Makes sense to test the inner unconnected component when you want a focused unit test on the component. FWIW, I greatly prefer integration tests (using JSDOM and enzyme) when working with React/Redux, since they're often easier to write than unit tests and give me much more confidence in the software, and they don't have the speed or reliability downsides that some people talk about with integration tests. Unit tests certainly have their place, though.

As one (admittedly extreme) example, a while back I hit an issue where a refactor caused a very simple crash: just load the app, click a particular button, and it immediately crashes. All relevant components, actions, stores etc had extensive unit tests, but there was no test to make sure that the component dispatched an action that was compatible with what the store wants. I wrote an integration test for it, "load the app and click this button, and assert the intended effect", and it ended up being about 5 lines. It exercised the same code paths as the hundreds of lines of unit tests, and gives me much more confidence in the overall correctness of the system.


Yeah I've had the same kind of problem: 100% test coverage by unit testing but I still had crashes because I wasn't testing integration.

I'd like to move to a page-based testing, where I mount the entire page and interact with it, asserting what's going on visually and in the store


You're right, but testing the whole page's component functionality in large frontend apps isn't super common, either. In these cases, you would test the lower-level components and then not necessarily test their functionality for the whole page.

The costs of deep prop trees 1) kills many performance benefits offered by a good react/redux setup 2) makes it hard to build off of the state (much like a monolithic codebase) because it can be harder to find if something is already there or not.


I'm a non-React dev but I wrote modern apps with Angular (and have written React). I think the best testable approach really is:

1. unit testing on the smallest bits (Redux actions/reducers make unit testing them SUPER easy)

2. do e2e with something like Cypress.

Anything in between can be helpful but I found it can be too tedious, or difficult to do and ultimately, unit/e2e catches most things


I think this is a solid approach. I like to add snapshot testing for individual components too. The slowest tests to write for me are always the ones that involve some kind of async stuff.


redux-saga-test-plan has a few warts, but it's made testing HOC interactions fairly straightforward for me.


This is great. Love the idea of passing components as props regularly. I have done this a few times before, but I guess in my head I still think of components as something "real" and so passing them down as prop data feels wrong somehow...but it isn't! Now that I have seen Dan Abramov bless it, I feel less guilty.

Off to refactor...


It's quite trivial to hack your own react-redux using the new context api. It makes a lot of sense too - you get type safety, redux state management, and the redux dev tools. Although I do worry that old-mate react-redux hasn't transitioned yet for a good reason.

Anyway, as soon as I realised that the two are complimentary, I was always kind of upset at articles like this that misled me for so long.


Please see my other comments in this thread about our current work to refactor React-Redux to use new context.

FWIW, it's not "just" about new context - there's a lot of other aspects we have to take into account, including trying to figure out how to best make React-Redux work with the upcoming "async rendering" capabilities of React, as well as the multitude of ways the community is using React-Redux beyond just the standard patterns.


Yes, and I'm really excited to see it. Not to say I don't already use react-redux, it's simply a good academic process to write your own.

It's good to know that everyone is together with bringing on async rendering. Thanks for your efforts!


Redux solves the problem of managing state and state changes.

Context solves the problem of passing props to nested components.

Context doesn’t replace Redux because they solve different problems. This is also evident in the fact that Redux internally uses Context to pass props.



See the other comments in this thread from Tim and myself about updating React-Redux to use `React.createContext`.

Also, as a side note: we're open to adding a render props-based API at some point in the near future, since there's a number of people who have expressed interest in that.


Something like the Consumer usage, and a way to apply it to arbitrary transformations of state (see the README on that repo for an example), would go a long way towards de-boilerplating a lot of things. The code I deal with has a painful number of connect() calls just for outputting pieces of data from the store.


Mmm... not sure I really see a difference between that and what `connect` does right now. Can you clarify with a specific example?


Compare...

    // with connect
    import { connect } from 'react-redux'
    import { defaultSomething } from 'src/store/ducks/something'

    const Whatever = ({ something }) => <div>{something}</div>;

    export default connect(state => ({
      something: state.something || defaultSomething
    }))(Whatever);
...to...

    // with consumer
    import { identity } from 'ramda';
    import { SomethingConsumer } from 'src/store/consumers'

    const Whatever = () => <div><SomethingConsumer>{identity}</SomethingConsumer></div>;
    export default Whatever;
One more line in the second also goes away if the consumer defaults to an identity function if not given a suitable child.

    // with consumer
    import { SomethingConsumer } from 'src/store/consumers'

    const Whatever = () => <div><SomethingConsumer /></div>;
    export default Whatever;
The boilerplate in the first case also gets much, much worse if you're using Typescript, as type inference gets wobbly at best with HOCs and so for even simple usage you have to also define out your "inner" and "outer" props for each component you want to use connect with.


So... 5 lines of code, vs 4 or 3?

Perhaps it's just that I'm very used to `connect` as the "standard" way of doing things, or that I don't use TS myself, but I'm really not seeing that much of a change in those examples (especially if you're importing pieces from other files that already abstract some aspect of the process).

After we get done with version 6 (update to use new context), we are open to ideas for new API approaches for a v7. If you've got specific suggestions, please either file an issue, or wait for us to open up some discussions and give us your thoughts then.


Is there something equivalent to the Context API in Vue.js?


> Is there something equivalent to the Context API in Vue.js?

Don't think so, but I still feel like Vuex is better than Redux and Context API.


provide/inject allows dependency injection, though it is not meant for application state


  // Note that it's named with UpperCase, not camelCase
Refers to Pascal case


How do you test all this magic going?


My rule of thumb: if you need Redux or the React Context API then your app is too big to be worth writing.




Applications are open for YC Winter 2019

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

Search: