How we’re persisting and hydrating React Native state asynchronously

Mark Goldstein
clairdev
Published in
3 min readJan 3, 2022

At Clair, we’re leveraging React Native & React Native Web to change the way people get paid. Naturally, as our apps have grown in complexity, we’ve found the need to persist and hydrate the application state between sessions/reloads. With a quick Google, it becomes apparent that there is no definitive guide that React gives us on how to solve this problem. For better or worse, that’s led us to create our own patterns on how to solve this issue.

We’ve reached for the following stack:

  • React’s ContextAPI to provide a shared state (state management).
  • Async Storage to persist shared state asynchronously.
  • React Navigation to provide a top-level insertion point to hydrate our shared state (context). You could easily use any web-based navigation too, like react-router.
  • Optional — React Query on an as-needed basis to minimize unnecessary storage of server state in your context. Check out this great talk on the topic of server vs global state. We won't use it here, but we highly recommend using it for production code.

From a high level here’s what’ll be happening:

Before the app mounts navigation, we get async state for each context (that we persist) using helper function hydrateState and provide that state to each Context’s Provider as asyncState. This happens in our App.js where we ensure the contexts are hydrated before mounting the navigation and rest of the app.

As the app mounts navigation, each Context’s provider initializes and brings the async state it received from the asyncState prop (which contain the context’s keys) using an effect called useAsyncStateEffect. The effect takes a context’s dispatch and an action that has the type which will set the context’s state (ie SET_USER_STATE) and the payload, which will be the asyncState. After this effect runs, the provider has is hydrated with the async state, and the provider provides its state!

If you just want to see working code, here’s a snack of this code in action!

Writing the code

UserContext

  • First, we define each context’s keys and default value so that we know what to get from async storage for that context.
  • Then, we create a user provider that uses our useAsyncStateEffect and provides the async state.
  • Finally, we create a reducer that sets our context and the async storage for that context’s updated state.

App

Now in our App.js it all comes together. We have an effect that blocks navigation from rendering until our contexts are hydrated and our top-level state is ready to go!

That effect and helper functions are just a few lines of code!

useAsyncStateEffect

hydrateState

And that’s all there is to it! Remember, there’s a snack with all this code in action, check it out!

We’re hiring! Check out our careers page for more information about our open roles. Follow us on Twitter and LinkedIn to keep up with the latest Clair news.

--

--