-
Notifications
You must be signed in to change notification settings - Fork 50.9k
Closed
Description
Essentially, I see useDeferredValue being stuck and never catching up.
Not sure if it's possible to extract it out of a Next.js app. I think it's probably a React bug because it seems related to core APIs, but I couldn't simplify it past the "render some JSX from server action" repro case.
Demo
Type "hello world".
In dev (npm run dev), the second text area catches up.
In prod (npm run build + npm start), the second text area often gets stuck and never catches up.
bug.mov
Code
Here is a repro case: https://github.com/gaearon/react-udv-bug/
This is the main harness:
export function TestPreviewClient() {
const [promise, setPromise] = useState<Promise<ReactNode>>(initialPromise);
const deferred = useDeferredValue(promise);
function handleChange(value: string) {
setPromise(renderAction(value));
}
return (
<div style={{ padding: 16, fontFamily: "monospace" }}>
<textarea
placeholder="type here"
onChange={(e) => handleChange(e.target.value)}
rows={3}
style={{ width: "100%", fontFamily: "monospace" }}
/>
<div
data-testid="deferred"
style={{ border: "1px solid #ccc", padding: 8, minHeight: 40 }}
>
<Suspense fallback={<div>loading...</div>}>
<Resolved promise={deferred} />
</Suspense>
</div>
</div>
);
}Note these helpers:
export function ClientWrapper({ children }: { children: ReactNode }) {
const t = performance.now();
while (performance.now() - t < 2) {
// do nothing
}
return <>{children}</>;
}
function Resolved({ promise }: { promise: Promise<ReactNode> }) {
return <>{use(promise)}</>;
}And this is the server part:
async function AsyncChild({
children,
}: {
children: ReactNode;
}): Promise<ReactNode> {
await new Promise((r) => setTimeout(r, 1));
return children;
}
async function Item({ children }: { children: ReactNode }): Promise<ReactNode> {
return (
<ClientWrapper>
<AsyncChild>{children}</AsyncChild>
</ClientWrapper>
);
}
export async function renderAction(input: string): Promise<ReactNode> {
return (
<Item>
<div>
<Item>
<div>{input}</div>
</Item>
</div>
</Item>
);
}You can generally simplify this structure but then it will be harder to reproduce.
Reactions are currently unavailable