S
SolidJS2mo ago
Elipzer

How to use createMemo to process a resource with Suspense

In my component, wrapping my expensive function in a createMemo causes the parent suspense to trigger rather than the suspense in the component with the expensive function. Here's a simplified example of my component's behavior.
const ConfigureBarSearchMatches = () => {
const resource = createAsync(async () => {
await new Promise(resolve => setTimeout(resolve, 300));
return 'resource';
});
const expensive = () => {
if (!resource()) return null;
const end = Date.now() + 300;
while (Date.now() < end); // simulate 300ms cpu
return resource() + 'expensive';
};
const expensiveMemo = createMemo(expensive); // without this memo, I see "loading resource + expensive...", with it, I get the parent suspense
return (
<div class="matches">
<Suspense fallback="loading resource + expensive...">{expensiveMemo()}</Suspense>
</div>
);
};
const ConfigureBarSearch = () => {
// if I use createMemo, I need to add a suspense here or else it falls back to the root suspense.
return <div><input /><Suspense fallback="parent suspense..."><ConfigureBarSearchMatches /></Suspense></div>
}
const ConfigureBarSearchMatches = () => {
const resource = createAsync(async () => {
await new Promise(resolve => setTimeout(resolve, 300));
return 'resource';
});
const expensive = () => {
if (!resource()) return null;
const end = Date.now() + 300;
while (Date.now() < end); // simulate 300ms cpu
return resource() + 'expensive';
};
const expensiveMemo = createMemo(expensive); // without this memo, I see "loading resource + expensive...", with it, I get the parent suspense
return (
<div class="matches">
<Suspense fallback="loading resource + expensive...">{expensiveMemo()}</Suspense>
</div>
);
};
const ConfigureBarSearch = () => {
// if I use createMemo, I need to add a suspense here or else it falls back to the root suspense.
return <div><input /><Suspense fallback="parent suspense..."><ConfigureBarSearchMatches /></Suspense></div>
}
[For context, the actual component is fetching a JSON dataset (resource), priming a MiniSearch index (expensive), and then executing a search based on an input signal (omitted)] Without the createMemo I get the "loading resource + expensive..." that I want, but I need the memo for some further processing on the "expensive" function. Is there a way to get my intended behavior using createMemo but without needing a suspense in the parent component?
4 Replies
Madaxen86
Madaxen862mo ago
That’s because Memos are eager in solid 1.0 meaning they are evaluating before being called. they will be lazy in 2.0 meaning being evaluated on first call. What you can do is: a) offload the expensive part to the async part inside the createAsync b) pass the data to a child component and memo there (so it’s inside the Suspense boundary) c) if you only call it once derive it without createMemo. As components won’t rerun you’ll only need to memo if you call the value multiple times.
Elipzer
ElipzerOP2mo ago
Ah, so it’s getting called before the child suspense exists! Looking forward to 2.0’s lazy so I can use my original idea. For now, I’m taking the B approach w/ a suspense in the parent. Thank you
Madaxen86
Madaxen862mo ago
d) would be using Show as
<Suspense>
//...
<Show when={data}>
{(d) => {
const expensive = createMemo(() => d()...)
return <div>{expensive()}</div>
}
}
</Show>
//...
</Suspense>
<Suspense>
//...
<Show when={data}>
{(d) => {
const expensive = createMemo(() => d()...)
return <div>{expensive()}</div>
}
}
</Show>
//...
</Suspense>
Elipzer
ElipzerOP2mo ago
Interesting. Using that Show doesn’t render its children until it is time to show. Nice that this works same-component even in 1.0. Also I like that the memo won’t run at all until after the data is loaded.

Did you find this page helpful?