Disclaimer: this post series is discussing alpha features of React. A lot of this is up in the air and is being actively discussed by the React team and the community. The features, problems, solutions, opinions and decisions that these posts discuss might not be final. This is as a summary of the trade-offs being discovered and discussed. I find these behind the scenes discussions to be interesting and educational. Writing these posts is my way of understanding the topics in more depth. Finally, the content is interleaved with with my personal experience and opinions. I hope you enjoy!
This post was in part prompted by the “Provide more ways to bail out inside Hooks” discussion on React repo. Note, I haven’t actually read the whole thing yet đź¤, those threads get pretty long. Update: I read it all by the time I finished Part 3 in this series.
I saw at least 2 aspects being discused in this thread. One is people attempting to use React’s new Context API and Hooks as a replacement for Redux and comparing the capabilities of both approaches. But are these APIs meant to be a replacement for Redux or are they simply complementary? Second aspect of the thread is a discussion among library implementors to identify any gaps in the new React APIs. I’ll focus on the first aspect in this post and the second aspect in the next post.
Even before this thread, I heard similar statements from several people now:
- Redux is no longer needed now that React has Suspense
- React’s new Context API can be used instead of Redux
- You don’t need Redux in Reason React because it has Redux built in
I disagree slightly and here’s why.
I love the direction React team has been taking. Hooks in particular make your code incredibly expressive, succint and composable. And writing all of your components as functions feels great. Not only do Hooks solve multiple problems in React, such as “Wrapper Hell” or “Huge components”, they’re also an improvement over the previous versions of the equivalent APIs.
For example, useState
is a better setState
, because you’re not limited to
using an object for your state and you get to name your state and setters using
more meaningful variable names, and of course, composability.
Similarly, useContext
is a better version of the old Context API, since it
subscribes to changes no matter how deeply your components are nested.
But does useState
, useContext
and useReducer
remove the need to use Redux?
Well, it depends on your application. It’s great to have more powerful and
cleaner primitives in React. These primitives can be very useful when starting
out, and they’re also great building blocks for more complex, use case specific
APIs.
And that’s the thing — in my opinion React has excelled in it’s ability to focus on the core set of primitives and let the communities build around their own use cases. I’m confident the React team will resist adding too much API surface area.
If your application benefits from Redux, I’d argue it’s not the best idea to migrate away from it in hopes that you can just use React’s new features instead. Redux does quite a bit under the hood to create an expressive API and good performance. If you swap Redux with your hand rolled Context and Hooks based store, you might run into sveral pitfalls (More on that in Part 2 of this series).
Implementation details aside, there’s a larger question at play here of whether
Redux style global stores are a good approach to modeling applications. Local
state as encouraged by the useReducer
hook, React Suspense or Reason React
seems to be a preferred approach by the React team. That is discussed more in
Part
3
of the series.
React Hooks will allow us to create nicer APIs for all of the existing state and
data management solutions. For example, if you’re using react-refetch
to bind
your components to REST APIs, instead of having to use a Higher Order Component
to connect, in the future you might instead do something like:
function MyComponent({ id }) {
const user = useResource(`/api/user/${id}`)
}
If you’re using GraphQL, perhaps something similar with Apollo:
function MyComponent({ id }) {
const user = useQuery(`query { user(id: $id) }`, { id })
}
And none of this means you can’t or shouldn’t be using Redux. In fact, Hooks will make using such stores really nice in the future:
function MyComponent({ id }) {
const user = useStore(state => state.users[id])
const dispatch = useDispatch()
}
Having said that, it’s likely that this new set of APIs – Hooks, Suspense might create entirely new patterns, approaches and libraries. But they will also make the existing tools better, because these APIs are a truer expression of what React is really all about.
Part 2 of this post goes into more technical details in exploring edge cases, limitations and open questions around usage of Hooks that I, as a library implementor, came across.
And finally, if you’re already using React 16.7.0-alpha and would like to use a global store via Hooks, checkout tiny-atom, a store similar to Redux, but with a more compact API.