S
SolidJS3mo ago
max

Mutate data for optimistic updates with createAsync

When using createResource you can use the mutate() function to update the value directly for optimistic updates. Is there a way to do this when using createAsync? For example in this example, I'd like to optimistically update the name() before I call my api function to update it.
const loadAboutData = cache(async () => {
return new Promise((resolve) =>
setTimeout(resolve, 500, "Solid")
) as Promise<string>;
}, "about");


export default function About() {
const name = createAsync(() => loadAboutData());
return (
<section class="p-8">
<Suspense fallback={<p>...</p>}>
<p>{name()}</p>
</Suspense>
<button
onClick={() => {
// optimistically update the dat
mutate((name) => “UpdatedName”)
// api call to update
}}>
Test
</button>
</section>
);
}
const loadAboutData = cache(async () => {
return new Promise((resolve) =>
setTimeout(resolve, 500, "Solid")
) as Promise<string>;
}, "about");


export default function About() {
const name = createAsync(() => loadAboutData());
return (
<section class="p-8">
<Suspense fallback={<p>...</p>}>
<p>{name()}</p>
</Suspense>
<button
onClick={() => {
// optimistically update the dat
mutate((name) => “UpdatedName”)
// api call to update
}}>
Test
</button>
</section>
);
}
13 Replies
lxsmnsyc
lxsmnsyc3mo ago
Not really. createAsync is just createResource under the hood, so it's either you use createResource or if your createAsync is tied to a cache, do cache mutations
max
max3mo ago
Thanks, I was reading through some github discussions and saw that there is a cache.set() method, do I just call this with the key value pair for the cache I want to update? Do I then need to call revalidate()? Also, just reading through the Cache Architecture on Github and it says: "cache API can be used with createResource but it must be passed into the tracking argument not the fetcher which means that you can't only pass a single argument as createResource defaults to just the fetcher. So to use a cache function with createResource you would need to:
const getUser = cache((id) => {
return (await fetch(`/api/users${id}`)).json()
}, "users") // used as cache key + serialized arguments

const [user] = createResource(
() => getUser(params.id),
v => v // pass the return promise through
);
const getUser = cache((id) => {
return (await fetch(`/api/users${id}`)).json()
}, "users") // used as cache key + serialized arguments

const [user] = createResource(
() => getUser(params.id),
v => v // pass the return promise through
);
" Is that all that needs to be done to use cache with createResource?
GitHub
RFC: Cache/Preload Architecture · solidjs solid-router · Discussion...
Summary In hopes of aligning with a future where cache centric approach is to routing this RFC outlines a change in Router architecture to enable optimal data fetching patterns for CSR & SSR SP...
peerreynders
peerreynders3mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
max
max3mo ago
Thanks!
peerreynders
peerreynders3mo ago
The above example is flawed given that the underlying resource's signal will not propagate the update when the value "doesn't change". So when you click "Test" a second time it will never update to the cached "third" from the optimistic "second". To address this it becomes necessary to "bust" resource's equals behaviour: https://playground.solidjs.com/anonymous/67657955-297e-4ef9-af12-4f35f606bd7e It also needs to be understood that this defeats suspense and transitions; but the objective here is to show an optimistic value, i.e. replacing the paint holding behaviour of the transition with a value that will be showing soon anyway.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Brendonovich
Brendonovich3mo ago
i think solid primitives has createWritableMemo just for this purpose https://primitives.solidjs.community/package/memo#createwritablememo
Solid Primitives
A library of high-quality primitives that extend SolidJS reactivity
peerreynders
peerreynders3mo ago
Yes it's a lot cleaner https://playground.solidjs.com/anonymous/aba5c525-b01e-4da1-be1d-9b603567b95c but 1. You still have to "bust the resource equals" otherwise the memo will stay on the last set value rather than change to the last fetched value (which matches the value in cache). 2. You also have to "bust the mutation equals" otherwise the memo won't change to an optimistic value that was already previously set but overwritten by a subsequent dependency change. 3. Suspense/transitions still don't apply.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
peerreynders
peerreynders3mo ago
i.e. let the UI handle the "optimistic UI" DON'T corrupt the reactive data flow with optimistic values. https://playground.solidjs.com/anonymous/88733aaa-51f4-4d06-abad-841153bf37a7
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
peerreynders
peerreynders3mo ago
So in the createAsync scenario instrumenting the "fetcher" may be one way of addressing the current limitations. https://playground.solidjs.com/anonymous/b033bb58-c7e3-4e47-a213-c6d6a83a5de5
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
mdynnl
mdynnl3mo ago
https://stackblitz.com/edit/vitejs-vite-jiumjg?file=src%2FApp.tsx i haven't been catching up with new stuff lately so i might be completely missing something
peerreynders
peerreynders3mo ago
Thank You for this. It's nice to have an example that places these primitives into their intended roles and then demonstrates how they are meant to collaborate with one another while also highlighting the idiomatic design they are suggesting. I find it difficult to glean that from the primitive-by-primitive documentation. I think single flight mutations is the most recent addition. However what still isn't clear is the implicit connection between cache and action because of this; and that in the absence of any keys any action will trigger all cached values (which thank to your example I was able to discover with some poking and prodding). So in the simplest terms the response to the OP should have been: 1. Use an action for the mutation 2. Use an associated submission.input as the optimistic value and show it while submission.pending
GitHub
Release v0.6.0 - Take Flight · solidjs/solid-start
Listening carefully to the feedback and reflecting on the second month of beta 2 we have prepared this release which improves on a lot of the usability around Start itself. This is less about featu...
GitHub
solid-router/src/data/action.ts at e773947b85ac78281816e86621a2cdb6...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
peerreynders
peerreynders3mo ago
The correct answer should have been: 1. Use an action to perform the mutation 1b. The action will automatically invalidate the cached value the createAsync draws on once the action completed. This causes the cached function to run again. 2. Use a submission linked to the action to a.) track it's pending state to know for how long the to show the optimistic value and b.) provide the optimistic value that was given to the action. Example code here: https://stackblitz.com/edit/solidjs-templates-7u2yad?file=src%2Fapp.tsx
peerreynders
StackBlitz
solid.js action cache interplay - StackBlitz
A Solid TypeScript project based on @solidjs/router, solid-js, typescript, vite and vite-plugin-solid