1import { useLayoutEffect } from "react";
2import { describe, expect, it } from "vite-plus/test";
3
4import { render } from "../../test/render";
5import { useStableCallback } from "./useStableCallback";
6
7function Child({ value, onLayout }: { value: string; onLayout: (childValue: string) => void }) {
8 useLayoutEffect(() => {
9 onLayout(value);
10 }, [value, onLayout]);
11 return null;
12}
13
14function Parent({ value, seen }: { value: string; seen: string[] }) {
15 const read = useStableCallback((childValue: string) => seen.push(`${childValue}->${value}`));
16 return <Child value={value} onLayout={read} />;
17}
18
19describe("useStableCallback", () => {
20 // Child layout effects run before the parent's, so a position restore in a
21 // child must still reach the handler of the render it commits with.
22 it("calls the committing render's function from a child layout effect", async () => {
23 const seen: string[] = [];
24 // A plain hook test; a router would add its own commits around the rerender.
25 await using screen = await render(<Parent value="first" seen={seen} />, { router: false });
26 await screen.rerender(<Parent value="second" seen={seen} />);
27 expect(seen).toEqual(["first->first", "second->second"]);
28 });
29});