Is there a way of dynamically updating `createFetcherStore` inputs?
taktran opened this issue · 2 comments
Hi,
I'm trying to figure out how to update createFetcherStore
inputs, when react props change, but can't quite figure it out.
Here is the code:
export const [createFetcherStore, createMutatorStore] = nanoquery({
fetcher: (...keys: string[]) => fetch(keys.join('')).then((r) => r.json()),
});
const createStore = (tag: string) => () => {
const store = createFetcherStore<{ content: string }>([
`https://api.quotable.io/random?tags=`,
tag,
]);
return store;
};
function Quote({ tag }: { tag: string }) {
const [$tags] = useState(createStore(tag));
const { data, loading } = useStore($tags);
return (
<p style={{ marginBottom: '20px' }}>
{loading ? 'loading' : data?.content}
</p>
);
}
when tag
changes in Quote
, createStore
won't update and hence the query is not refetched. How would I do this?
Here is an example: https://stackblitz.com/edit/react-ts-bbwme2?file=App.tsx
Notice that the headings change, but the quote is not refetched when changed in the select box.
@taktran Hi there!
That's quite simple: the useState
hack should rarely be used, and you should always remember that the underlying function (the return of createStore
in your case) isn't rerun when props change, it's a factory function in useState
that's only called once (twice in StrictMode
during development). So props are not passed to the store automatically.
In your case the simplest solution would be to manually sync the state of the closure with the state of the component with an effect. Basically, you need to create a bridge between two reactive paradigms: the react one and the nanostores one. Treat the createStore
function as a JS module that's scoped to a component instance. You can use all reactive features of nanostores there, lifecycle hooks and new stores as well!
export const [createFetcherStore, createMutatorStore] = nanoquery({
fetcher: (...keys: string[]) => fetch(keys.join('')).then((r) => r.json()),
});
const createStore = (tag: string) => () => {
const $tag = atom(tag);
const store = createFetcherStore<{ content: string }>([
`https://api.quotable.io/random?tags=`,
$tag,
]);
return { store, changeTag: $tag.set };
};
function Quote({ tag }: { tag: string }) {
const [{ store: $tags, changeTag }] = useState(createStore(tag));
const { data, loading } = useStore($tags);
useEffect(() => {
changeTag(tag);
}, [tag]);
return (
<p style={{ marginBottom: '20px' }}>
{loading ? 'loading' : data?.content}
</p>
);
}
(it goes without saying that the best way to avoid all of this would be to put as much state as possible into JS; but in your case I think that's impossible)
I think, I answered your question, but if something's unclear feel free to reopen the issue 👍
Amazing, thanks for the speedy response! 🏎️
I've updated the stackblitz with the fix: https://stackblitz.com/edit/react-ts-qjlkoh?file=App.tsx