2. async-states: Using react
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:
- async-states: the state management library
- async-states: Using react
- async-states: The source object
- async-states: The producer
- async-states: Advanced concepts
- async-states: vs react-query vs redux vs recoil
- 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
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:
- A
string
that identifies a state by key - A
producer
function - A
source
object from eithercreateSource
,Sources.for
oruseAsyncState
- A
configuration
object that combines the whole producer configuration and adds special ones for react’s usage.
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:
To raise the bar even higher, here is a producer that updates the state on an interval period:
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 viasuper-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 :
If you are using react-router
‘s loaders and actions, then you can simply do something like this:
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:
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!
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:
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 callon
, 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 propertyevents
allows configuring events of types:change
andsubscribe (when your component actually subscribes to the state instance)
.change
can be used as callbacks when the state changes, andsubscribe
can be used to attackhost
specific events; likeonFocus, onBlur
… and so on. - From
useAsyncState().onChange
oruseAsyncState().onSubscribe
that add additional events like from the configuration property.
Let’s take a look at some examples:
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:
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:
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:
Cache
You must be aware already of cache from the previous examples. The internal type behind it is:
So basically you control:
enabled
: Whether to enable cache or not, must be specified explicitly in case oftrue
getDeadline
: A function that returns the amount of Millis after which the state data should be deleted (if requested)hash
: A function that receives theargs
andpayload
of the run and calculated its cache hashpersist
: 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.