“What?!”, you say. “GraphQL is a server-side query language. Redux is a client-side state management library. How could one replace the other?” Good question.
Hold onto your butts, because I’m about to answer it.
⚛️ Switching to React
First, a little back-story. Back in 2016, the front-end team at Pathwright began switching our client-side code from a Backbone & Marionette stack to React. The declarative model for UI made much more sense than the MVC patterns we’d been dealing with.
It was a breath of fresh air and still is to a large degree.
Everything was beautiful except for the state management side of our app. We turned to the Flux architecture quickly, and it felt like a major improvement at first. The concepts of circular data flow and single source of truth were philosophically sound and at least saner than the model-centric view binding approach of most MVC libraries.
However, as our state management needs grew more complex, it started to feel like more and more layers of indirection. Once you have a handful of stores or branches in your state tree, you end up practically duplicating your server-side business data and relationships on the client.
We had these beautifully declarative React components with a data layer that became a rats nest of actions, reducers, async middleware, and de-normed business data/logic.
This all felt very wrong.
↪️ Switching to GraphQL
That’s when we tried GraphQL. We began by implementing it on a new dashboard that combined a bunch of different data sources (this would have been a nightmare to do with our RESTfull APIs) and soon fell in love. It was like discovering React for the first time. Enthusiasm was high enough that we ended up getting our newly minted GraphQL server in production in only two weeks!
Soon after, we started replacing a bunch of our REST APIs with GraphQL and it continued to be amazing.
One of the side effects (sagas?) of this was that our UIs using these new GraphQL endpoints no longer needed stores at all. We started like we normally would with new stores, actions etc., but ended up deleting them because there simply wasn’t anything for them to do.
🤯 Three Startling Realizations
This led to three startling, yet obvious in hind-sight, realizations for us:
Most of our state management code was concerned with merging and mutating data from discrete REST resources into the right shape for our UI (reducers, selectors, actions etc.).
A lot of our most complex state management was trying to manage the asynchronous nature of getting all that data in the right order for a specific route (sagas, middleware, thunks etc.).
Practically everything else, UI state, worked great with plain old React state.
“Damn”, we said, “damn,” while plunking coins into the swear jar.
And then we deleted a lot of code. It felt good.
So about GraphQL and Redux…
My title was a little mis-leading (made you click?). What we really replaced was our REST API and then found as a result that most of our state management code was no longer necessary.
When the client can control the exact shape of the state it needs from the server and get it all back in a single request, there’s simply not much need for state management libraries.
To illustrate the point, let’s pretend our UI is having a conversation with our backend service through the state management library we’re using.
Here’s what that might look like:
Redux + REST left — Apollo + GraphQL right
So much of the work done by Redux and side-effect management libraries is trying to simplify that left-most conversation above.
I would argue that for most client-side apps, GraphQL can replace the need for Redux entirely.
I’m not saying Redux doesn’t serve a purpose. It’s a great library and the state management patterns it’s introduced are going to be around for a long time.
Sometimes you don’t have control of your back-end stack and you’re forced to force REST to behave for your client-side UIs. That sucks and Redux really helps in that environment.
There are also cases where you’re managing very complex state that needs trackable and consistent control: lower-level things like a client-side cache, offline syncing, etc. Redux is great for these cases. In fact, some popular GraphQL libraries like Apollo can use Redux under the hood as a cache.
If you can use GraphQL instead of REST, you should. Switching will get rid of a huge amount of complexity in your client-side state management and reduce the scope of your client-side code to just how data should render in the UI (which is what it should have been all along).
Oh, and you can still use Redux alongside GraphQL, you just won’t need it very much. Plus you may be able to delete half your code which feels awesome.