Abhishek
Abhishek10mo ago

React Suspense is not working up with convex

I wrapped my client parent component with suspense, the parent client component is using useQuery hook and and based on state the query is changing. I want to ask am I doing it in a wrong way cuz my suspense fallback is clearing not showing up This how i am wrapping my parent component
<main className="flex min-h-screen flex-col items-center">
<TrendingSection />
<Suspense
fallback={<div className="h-40 w-40 bg-slate-400">Loading...</div>}
>
<WeekHeader />
</Suspense>
</main>
<main className="flex min-h-screen flex-col items-center">
<TrendingSection />
<Suspense
fallback={<div className="h-40 w-40 bg-slate-400">Loading...</div>}
>
<WeekHeader />
</Suspense>
</main>
6 Replies
Abhishek
AbhishekOP10mo ago
If it helps my parent component ie "WeekHeader" has a client component that is also using useQuery to fetch info based on the prop passed from parent
Michal Srb
Michal Srb10mo ago
You need to implement a version of useQuery that uses Suspense. The built-in one does not use Suspense, and returns undefined while the query is loading.
Abhishek
AbhishekOP10mo ago
@Abhishek any sample or code example you have, It will be of a great help to speed up my progress thanks
erquhart
erquhart10mo ago
Unfortunately, React docs state "Suspense-enabled data fetching without the use of an opinionated framework is not yet supported". You'll need to switch to React canary releases and use the experimental use() API to make this happen. If you're using Next, though, they may provide a way. I'm not sure how Next implements suspense enabled data fetching or whether it's compatible with a custom hook.
Michal Srb
Michal Srb10mo ago
This is an example that also used SSR in Remix, you can get rid of the SSR part. Right now suspense is just throwing a promise:
export function useQuery<Query extends FunctionReference<"query">>(
query: Query,
...args: OptionalRestArgs<Query>
): Query["_returnType"] | undefined {
const ssr = useAsync(
[getFunctionName(query), convexToJson(args[0] ?? {})],
() =>
typeof window === "undefined"
? (globalThis as unknown as { __ctx: ActionCtx }).__ctx.runQuery(
query,
...args
)
: undefined
);
if (typeof window === "undefined") {
return ssr;
}
const convex = useConvex();
const live = convexUseQuery(query, ...args);
const liveOrSsr = live === undefined ? ssr : live;
// if (promiseRef.current !== undefined && live !== undefined) {
// promiseRef.current(live);
// }
if (liveOrSsr !== undefined) {
return liveOrSsr;
}

// Both live and ssr are undefined, this is a clientside
// transition, suspend!
let resume;
const watch = convex.watchQuery(query, ...args);

watch.onUpdate(() => {
resume!();
});

// END TODO

throw new Promise((resolve) => {
resume = resolve;
});
}
export function useQuery<Query extends FunctionReference<"query">>(
query: Query,
...args: OptionalRestArgs<Query>
): Query["_returnType"] | undefined {
const ssr = useAsync(
[getFunctionName(query), convexToJson(args[0] ?? {})],
() =>
typeof window === "undefined"
? (globalThis as unknown as { __ctx: ActionCtx }).__ctx.runQuery(
query,
...args
)
: undefined
);
if (typeof window === "undefined") {
return ssr;
}
const convex = useConvex();
const live = convexUseQuery(query, ...args);
const liveOrSsr = live === undefined ? ssr : live;
// if (promiseRef.current !== undefined && live !== undefined) {
// promiseRef.current(live);
// }
if (liveOrSsr !== undefined) {
return liveOrSsr;
}

// Both live and ssr are undefined, this is a clientside
// transition, suspend!
let resume;
const watch = convex.watchQuery(query, ...args);

watch.onUpdate(() => {
resume!();
});

// END TODO

throw new Promise((resolve) => {
resume = resolve;
});
}
erquhart
erquhart10mo ago
throwing a promise 🤦‍♂️

Did you find this page helpful?