2. async-states: Using react

Mohamed EL AYADI
7 min readFeb 20, 2023

--

In this post, you will discover how to use the async-state toolbox to manage state with react, via the state hook useAsyncState.

This post is a part of the following blog post series:

  1. async-states: the state management library
  2. async-states: Using react
  3. async-states: The source object
  4. async-states: The producer
  5. async-states: Advanced concepts
  6. async-states: vs react-query vs redux vs recoil
  7. react-async-states: SSR

Plan

  • Introduction
  • Using react and the core library
  • useAsyncState
  • useSource
  • useProducer

Introduction

async-states was written initially as a proof-of-concept in the react land. The very first idea was the API presented in the previous post that basically answers all of our needs. Then it matured enough to be a standalone package working in all environments with low level state manipulation toolbox (internal rewrites are more than 7 times, without impact on external API).

When building user interfaces, the most common use cases are a subscription to a state, reading its data and probably manipulating it from different places. The library covers these basic needs via a single hook called useAsyncState, that accepts two arguments:

useAsyncState basic signature

useAsyncState uses the same signature as most of react hooks, so the usage is slightly similar. There is an important difference is that deps for the library defaults to an empty Array ([])rather than undefined. It is used to refresh your configuration in a dependency basis, like the react’s model.

The create parameter can be:

This allows the library to spawn your state with full control in any component or any execution context with contextual payload and arguments!

No limitations and no tricks!

using react and the core library

If you remember from the previous post, there was a usage in react directly with the core library that does not depend on react, you can do things like this:

useSES and source

To raise the bar even higher, here is a producer that updates the state on an interval period:

counter interval producer

Test it here! I even wrote a websocket gateway using this pattern! (The devtools also uses something similar).

You also subscribe from vanilla javascript or any other application in the window. We can also make producers that post messages to other windows/workers and waits for their answers. These examples will be revisited in the producer blog.

useAsyncState

Coming back to this magical hook, it allows you to

  • Create states accessible anywhere that produce values via super-powered producers.
  • Subscribe to them if they were previously created.
  • Wait for them if you wish
  • Completely have full-control over the state

Here are the most common examples:

Routing based

The common way to perform search and shareable links is to append contextual things to your URL, then react to their changes through your favorite router. Here is how you can achieve it :

Routing based

If you are using react-router‘s loaders and actions, then you can simply do something like this:

Usage with loaders

Optimistic UI

Being able to perform optimistic UI was one of the very first goals of the library.

The producer is fed from your args and payload that it receives via its props parameter. In fact, a copy of this props object containing the args and payload is assigned to your state, every time.

If your state is success, you have the props that led to that success state (and also for error and aborted statuses).

If we look back to our user search example, we can do things like this:

props.args in optimistic UI

The state.props object contains payload and args, so depending on how your producer is defined (to use either of them), you will be able to know what it was passed and led to that state. In a consistent way!

Search-as-you-type

For sure you already saw this plenty of times already! That’s why I’m leaving this section to you, you can do it!

Hint for better UX

Shared states

All states are shared and can be subscribed from anywhere either by the string key or the source object.

Take a look at this example here:

Shared state

useAsyncState can receive just a state‘s (string) key as parameter and do the rest for you! Test the previous example here.

Events

Being able to listen and react to events is one of the most used features in applications. The library allows that from several places:

  • From the source object: you can use the on method to register events, the supported ones until now are: change (state change), cache-change (cache change), dispose(state is reset). When you call on, it returns its cleanup that will remove that event. Several events may be added such as: pre-change (with preventDefault and stopPropagation ;) ), pre-run (same) … and so on.
  • From useAsyncState configuration object: the configuration property events allows configuring events of types: change and subscribe (when your component actually subscribes to the state instance) . change can be used as callbacks when the state changes, and subscribe can be used to attack host specific events; like onFocus, onBlur … and so on.
  • From useAsyncState().onChange or useAsyncState().onSubscribe that add additional events like from the configuration property.

Let’s take a look at some examples:

useAsyncState({events}) example

Try it yourself here! (just search for an id bigger than 10 or an alphanumeric to simulate an error)

This is slightly equivalent to this:

useAsyncState().onChange example

The difference is that you can control when to call the onChange and it won’t depend on the render (but you should wisely put the deps for useAsyncState if you opt to this! use at your own risk!)

Note: Using events like this can be pretty bad if you have multiple instances declaring this event and connected to the same state: they will be all executed. To address this, the runc function exists!

Now let’s take a look at subscribe. Let’s say, for the previous example, when you quit and come back to the tab after one second, we refresh the latest fetched data -Well, Im really annoyed by this feature being on every social media website these days!). This can be as simple as this:

onSubscribe example

Play with it here! Please note that onSubscribe and events.subscribe are the same as onChange and events.change

Without using react, you can fall back to the low level source API: source.on. It is used like this:

source.on example

Cache

You must be aware already of cache from the previous examples. The internal type behind it is:

cache config t

So basically you control:

  • enabled: Whether to enable cache or not, must be specified explicitly in case of true
  • getDeadline: A function that returns the amount of Millis after which the state data should be deleted (if requested)
  • hash : A function that receives the args and payload of the run and calculated its cache hash
  • persist : In case you want to persist your cache, use this function that’s called everytime the cache changes, with all entries.
  • load : A function that initially loads the cache (may be async)
  • onCacheLoads : When cache loads asynchronously, you can attach this event that would alter the state for example.

Note: The initialValue configuration of the state receives the cache, so if you can synchronously load a cache, you can boot your state from it.

useSource and useProducer

As from their names, these hooks are self explanatory and are written like this in the library:

So, they are basically just different names now… (they were different in the past, same API but not same implementation).

Conclusion

If you think of a use case that I did not cover in the previous examples, please let me know so we can add it.

Next, let’s see how the source object works and how we can use it.

--

--