S
SolidJS•2w ago
fengzi91

How to invalidate cached data or show loading state during data filtering in SolidStart?

I'm working with a query in SolidStart and want to show a loading state when filtering data. Here's my current implementation:
const getTodos = query(async (type?: string) => {
// ...get data
return data;
}, 'todos')

export default function Home() {
const [searchParams, setSearchParams] = useSearchParams()
const todos = createAsync(() => getTodos())

return (
<div>
<div>
filter:
<button onClick={() => {
setSearchParams({...searchParams, type: undefined})
}}>all</button>
<button onClick={() => {
setSearchParams({...searchParams, type: 'completed'})
}}>completed</button>
</div>
<Suspense fallback={<div>loading...</div>}>
<For each={todos.latest}>
{item => <div>{item.title} - {item.completed ? 'completed' : 'pending'}</div>}
</For>
</Suspense>
</div>
)
}
const getTodos = query(async (type?: string) => {
// ...get data
return data;
}, 'todos')

export default function Home() {
const [searchParams, setSearchParams] = useSearchParams()
const todos = createAsync(() => getTodos())

return (
<div>
<div>
filter:
<button onClick={() => {
setSearchParams({...searchParams, type: undefined})
}}>all</button>
<button onClick={() => {
setSearchParams({...searchParams, type: 'completed'})
}}>completed</button>
</div>
<Suspense fallback={<div>loading...</div>}>
<For each={todos.latest}>
{item => <div>{item.title} - {item.completed ? 'completed' : 'pending'}</div>}
</For>
</Suspense>
</div>
)
}
Currently, the loading state only shows on the initial page load. When I click the "completed" filter button, it doesn't show the loading state - it just replaces the list when the new data arrives. I want to show the loading state when switching filters, essentially invalidating todos.latest when the filter changes. Any suggestions on how to achieve this? Thanks in advance!
6 Replies
Madaxen86
Madaxen86•2w ago
createAsync starts a transition for the accessor. So you can use useTransition to get a pending signal (which will trigger on updates not on the initial load
const Test = () => {
const [s, setS] = useSearchParams<{ q: string }>();
const data = createAsync(() => getData(s.q));
const [isPending] = useTransition();

return (
<>
<h1>Example filter</h1>
<input
value={s.q || ""}
onInput={(e) => setS({ q: e.currentTarget.value })}
class="rounded-md border border-gray-300 px-4 py-2 text-foreground focus:border-primary focus:ring focus:ring-primary/20"
/>
<Show when={isPending()}>
<div>Loading...</div>
</Show>
<Suspense>
<pre>{JSON.stringify(data(), undefined, 1)}</pre>
</Suspense>
</>
);
};
export default Test;
const Test = () => {
const [s, setS] = useSearchParams<{ q: string }>();
const data = createAsync(() => getData(s.q));
const [isPending] = useTransition();

return (
<>
<h1>Example filter</h1>
<input
value={s.q || ""}
onInput={(e) => setS({ q: e.currentTarget.value })}
class="rounded-md border border-gray-300 px-4 py-2 text-foreground focus:border-primary focus:ring focus:ring-primary/20"
/>
<Show when={isPending()}>
<div>Loading...</div>
</Show>
<Suspense>
<pre>{JSON.stringify(data(), undefined, 1)}</pre>
</Suspense>
</>
);
};
export default Test;
exercise
exercise•2w ago
but this transition can be triggered by other things, like actions 🤔 if you have an action in your page
fengzi91
fengzi91OP•2w ago
thx, I tested that useTransition doesn't work. I tested that action doesn't work well either.
Madaxen86
Madaxen86•2w ago
It does. Just as in the snippet. It won’t be pending on the initial page load as then the Suspense boundary is triggered and the fallback of the Suspense boundary is shown instead. On localhost the query might resolve so fast, that the loading fallback will just flick in for few milliseconds. Maybe you can share more of your code. You can add a ts createEffect(() => console.log("isPending",isPending()); to see if it’s getting triggered.
fengzi91
fengzi91OP•2w ago
I created a test project on Codesandbox. https://codesandbox.io/p/devbox/heuristic-dream-wcnvzy
Madaxen86
Madaxen86•7d ago
.latest will not trigger the transition and "stay in the past" until the new value is returned by createAsync Just call todos() in your todos/index.tsx

Did you find this page helpful?