-
-
Notifications
You must be signed in to change notification settings - Fork 589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Track async setter state #204
Comments
Something like this would do nicely: const [value, setValue, valueWriting] = useAtom(accessToken);
return (
<SettingsItem value={value} onValueChange={setValue} showLoadingSpinner={valueWriting} />
); EDIT: the type definition for |
export function useAsyncAtom<TValue> (atom: WritableAtom<TValue, TValue>) {
const [value, setValue] = useAtom(atom);
const [loading, setLoading] = useState(false);
const set = useCallback((update: TValue) => {
setLoading(true);
const asyncSetterState = setValue(update);
if (__DEV__) {
invariant(asyncSetterState == null, [
'useAsyncAtom: Handling a non-async setter. If your atom doesn\'t have an async setter,',
'you should probably call useAtom instead.'
].join('\n'));
}
if (asyncSetterState != null) {
asyncSetterState.then(() => setLoading(false));
} else {
// Immediately set the loading value to false.
// We shouldn't really be handling sync setters.
setLoading(false);
}
}, [atom, loading]);
return [value, set, loading];
} No idea if it works, but here's my first try. |
Exactly! That's how I expect for this use case. |
Hmm the problem with the implementation above is that it would only return loading, if the setter was called from within the same component. Not really sure how yet, in a generic way, but would be nice to have the loading state shareable throughout the app. The only way I can see to do that though, is to have a separate loading atom for each piece of data stored in an atom. |
This is no longer true. Related issue: #607
Yeah, like this, right? const dataAtom = atom(null)
const inProgressAtom = atom(false)
const asyncWriteAtom = atom(null, async (_get, set, url) => {
set(inProgressAtom, true)
try {
const response = await fetch(url)
const data = await response.json()
set(dataAtom, data)
} finally {
set(inProgressAtom, false)
}
}) Not too bad? One thing I suppose is the community is not yet ready for best practice about how to deal with Suspense and async actions. That said, jotai's async write will suspend, so you can do this instead. const dataAtom = atom(null)
const asyncWriteAtom = atom(null, async (_get, set, url) => {
const response = await fetch(url)
const data = await response.json()
set(dataAtom, data)
})
const Component1 = () => {
useAtom(asyncWriteAtom) // this will suspend
//...
}
const Component2 = () => {
useAtom(asyncWriteAtom) // this will also suspend
//...
} Does it look a generic way? FWIW, we have a new issue about async get without Suspense: #672 |
Yeah seems reasonable and you can always wrap this via a simple method to hide the implementation anyway! |
Hello @dai-shi, thanks for creating Jotai! I'm sorry for hijacking this issue, but I'm having troubles to understand your last response. I am also using an async setter, and I would like to track its execution state (for example, to disable a button while in progress). While I can use Suspense with atoms with async getters, I don't see it working with async setters following your last example. Here's a trivial example: const myAtom = atom("hello")
const myAtomSetter = atom(null, async (get,set) => {
await someAsyncFn()
})
const Content = () => {
const [,setter] = useAtom(myAtomSetter)
return (
<button onClick={() => setter()}>Press me</button>
)
}
const App = () => {
return (
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
)
} In this example, the Suspense fallback doesn't seem to trigger when the button is pressed. I can define an Thanks a lot for your work! |
Async write no longer suspends. |
Hello, and thanks for developing Jotai (it's awesome!) 👋
Although, I have a specific use-case where tracking the async setter state would be incredibly useful.
I use atoms for accessing data in persistent storage (e.g. IndexedDB), which isn't a synchronous API.
Therefore, I'd like to show a loading state between setting the value of an atom with an async setter,
and the value actually being reflected in storage.
Is this possible with Jotai?
The text was updated successfully, but these errors were encountered: