Skip to main content

Asynchronous Code

Using mutate()

Demo

Loading...
import { createReactRecorder } from 'diagon-react';
import React, { FC, memo } from 'react';

const { useSnap, useMutator, mutate } = createReactRecorder();

const state = { currentTime: '?' };

export const AsyncWithRecorder: FC = memo(() => {
const currentTime = useSnap(state, state => state.currentTime);

const fetch = useMutator(state, async (state) => {
state.currentTime = undefined;

const newTime = await fetchTime();

mutate(state, state => state.currentTime = newTime);
});

return (
<div>
<div>{currentTime || 'fetching...'}</div>
<button onClick={fetch} >Get time in 1 seconds</button>
</div>
);
});

const fetchTime = () => new Promise<string>(
r => setTimeout(() => r(new Date().toLocaleTimeString()), 1000));

The code first uses useMutator to bind to the top-level state which will allow for mutuations at the beginning of the fetch mutator. This is important and the only gotcha when dealing with async functions. Whenever your code returns from an await, or in a callback, the recorder is no longer keeping track of changes at that moment. In order to mutate state anywhere in your app, and therefore update components, you call the mutate function on the recorder with your state. The function you pass in may mutate the state as you wish. This is a very useful pattern, as it allows you to do multiple asynchronous operations between as many calls to mutate as you like. It also enables scenarios like batching.

One thing to keep in mind with asynchronous code, is that you'll notice in the demo that if you continually click the button, multiple promises will be executed and will each update the current time when they return. If this is undesirable, and you only wish to use the result of the latest user click, you can simply keep an identifier for each call to fetch and only update the state if the result corresponds to the latest id. Conversely, you may also prevent additional fetches until the first asynchronous call completes.