4. async-states: The producer
Promises, async/await, generators, reducers… or even nothing! These are all supported out of the box by the library.
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
- The producer types
- The philosophy
- Conclusion
The producer types
The producer is the function that does the work and produces the state value. It can be of the following types:
Function returning a Promise/thenable
If the producer function returns a Promise
, the state is captured when it resolves or throws.
Here are some examples:
Async/await function
async/await
basically returns a Promise
when invoked, so it just naturally works the same as promises.
Generator
Generators are supported and can be either synchronous or asynchronous (if you yield a promise, you go async). Generators will stop the work immediately and not yield when the run
gets aborted.
Synchronous function
It can be a function that returns a value immediately other than a Promise and a Generator, in this case, there is no pending
state and status will pass directly from initial|success|error
to success|error
.
Notice the createReducerProducer
that let you write in a reducer style or migrate incrementally an existing reducer pattern.
Nothing
Well, you can use no producer at all:
Note: run
in this case delegates its work to setState
.
The philosophy
While manipulating a state in general, there are three common ways to set the value of your data:
- You set it imperatively in there, for this, there is
setState
- You perform some work that gives you this value (
sync|async|generator
) and you get the result, for this there isproducer
and therun
functions - State is emitted from a remote place such as intervals, websockets or whatever. For this, until I figure out the
RemoteAsyncState
abstraction, there isemit
that can mimic this.
To support this, the idea is to create a function that accepts one single props
with enough power to cover all needs while having the ability to perform good user and developer experiences.
The producer’s power
Like a keep repeating, the producer’s power comes mainly from its props; Here are all the properties:
args: any[]
retrieved asarguments
ofrun
orrunp
; orautoRunArgs
fromuseAsyncState
. So when you perform a.run(arg1, arg2, arg3)
you would receive these values as an array calledargs
.payload: Record<string, any> | undefined
A shallow copy the payload present in the state instance at the moment of the run. You can set it manually viamergePayload
function; or while passing it touseAsyncState
(pay attention todeps
!)onAbort((reason?) => void): void
Let you register callbacks that will be invoked when the abort occurs. Should cleanup the work actually ongoing by the producer; for example aborting a fetch request, clearing a timeout/interval, disconnecting from a websocket, terminating a worker… and so onrun(input, config, ...args): AbortFn
Read about it here in the docs. It allows running any state and getting the abort function. (It can be chained likeprops.onAbort(props.run('state_key'))
which will lead to cascading cancellations.runp(input, config, ...args)
Read about it here in the docs. Similar to run, but returns aPromise
to the resolve of the state so that you can chain using.then
orawait
directly.select
Read about it here in the docs. Selects the current state of any stateemit
Read about it here in the docs: Emits updates to the state after the producer resolve; this allows changing the state from remote events like intervals or websockets clients!lastSuccess
The last registered succeeded value; useful for reducers or infinite listsgetState
Since producer may stay alive after return,getState
may be useful to get the current state at any point of time.isAborted(): boolean
Returns whether the current run was aborted or notabort(reason?: any): void
Aborts the current run, if it occurs before going pending, no state update will be performed and everything will be completely bailed out!
Note: If you throw inside the producer, the state will pass to error
with whatever thrown as data
.
Conclusion
The producer will cover your needs from having an imperative state without any “producer”, to all producer types to perform data fetching, external events atomic changes, events emission, proper cleanup registration and many more. All the way while allowing you to tune and customize your experience as seen from the previous posts.
Next, to push the bar even higher, you will learn about some advanced concepts that the library offers. Yes! all of what we’ve seen until now are basic state manipulation. We haven’t talked yet about forks
, lanes
or pools and clusters! Next blog please!