Handling data fetching with state machines
Fetching data just got a bit easier
I get jealous whenever I see people post their passion projects on Twitter. ✨ They typically have all of the fancy tech, with GraphQL, or super-interactive animations or just jaw-dropping performance. In my day-to-day job, I don’t handle any of those concerns. I fetch data.
The mundane reality of real-life apps
When I fetch data in one of my container components (let’s say, a user profile page), I know what to do. I’ve done it a ton of times. I scaffold out a component, complete with some state variables that typically are named data
, error
and loading
. I load some data when it mounts, and complete the boilerplate with some conditional rendering to render different things for different states.
They all kind of look like this:
There’s a ton of state, and it looks exactly the same. Every. Single. Time. Every single time I write this, you could bet your ass I forget to reset SOME part of that state for some particular case.
Say hello to state machines
Let’s make our lives a bit easier by creating a state machine! 💥
Each of my data-fetching components can be in one out of four states — it can be idle (before it has fetched its data), pending, successful or erroneous. It can go from idle to pending, and from pending to either successful or erroneous. Once there, you could reset the state back to idle if you want.
This starts to sound a lot like a state machine. I have to admit that my knowledge on theoretical state machines is somewhat limited, but this article by Mark Shead helped me out a ton! I suggest you spend a few minutes skimming through it if you’re like me.
So let’s start constructing our state machine. A simple outline would look like this:
As you can tell, there’s not a lot going on in here. You create an instance of this ApiStateMachine
thingamajig, and call the next()
-method whenever you want to make the state machine change states. It takes an optional input
parameter, which can indicate whether something went well or not.
The main point is that your state can be in one and only one state — it can’t be pending with an error, or idling with a result. You’ll never run the risk of forgetting any part of the state again!
Can I use this in React?
Yeah sure, why not? To make this a bit more React-y, we can make it into a higher order component:
This HOC accepts a component, and returns a new component that keeps all of this state for you, and provides both the state and a way to go to the “next” stage as props.
This way, we can rewrite most of our data-fetching pages like this:
Improving the API
Now this is starting to look pretty neat! The only thing I’m not very happy about is this .next()
method. It isn’t very easy to understand how it works. Let’s fix that.
Instead of the non-descript .next()
method, we can expose .pending()
, .success()
, .error()
and even an .idle()
method! Although not implemented above, our HOC can potentially make sure to warn us if we call them out of order too — which keeps the state machiny features alive for us.
Bottom line is, we still hide all of the complexity of state handling in a reusable HOC. Here’s the final no-frills version of the API state machine HOC:
This is how you’d use it:
Reads pretty nicely, doesn’t it? I think so too.
Here’s a simple example app where you can play with the result:
Can we do more?
There are of course tons of more cool stuff we could do to improve this naïve implementation, including adding types, dev-only warnings and perhaps a slightly more refined API. You could make it handle error messages for you, or even the actual data fetching as well if you’d so please.
The cool thing about HOCs — and React in general — is how well composition works. So instead of writing a component that “does everything”, you can write several smaller utilities that compose together to do what you need them to do in any given situation.
Here’s an example where I’ve implemented an HOC withData
that handles the actual data fetching and state of fetching stuff from an async source. Now, our actual page component can be stateless, and the data fetching logic is shareable!
Here, the withData
HOC uses the withApiState
HOC to handle the general use case, while still making withApiState
available for reuse on its own.
Summary
Separating out reusable logic like API data fetching states can save you a lot of troubles down the line. If you create a HOC to handle all that jazz for you, you can solve it once, and reuse it everywhere :)
Hope you got inspired to create your own API state handler, or just copy mine. Please let me know what you think in the responses, and please give me a few claps if you think this was worth your time. Thank you!
More where this came from
This story is published in Noteworthy, where thousands come every day to learn about the people & ideas shaping the products we love.
Follow our publication to see more product & design stories featured by the Journal team.