import { useEffect, useEffectEvent, useState } from "react";
import { Button } from "@/components/ui/button.tsx";
import { Typography } from "@/components/ui/typography.tsx";
import { useLogger } from "@/components/pages/tests/experiment.tsx";
type Theme = "light" | "dark";
// A fake chat connection. reconnect() fires every "connected" handler again,
// the way a chat library does after a network blip, without re-running the
// effect that registered it.
const connection = {
handlers: new Set<() => void>(),
on(_event: "connected", handler: () => void) {
this.handlers.add(handler);
return () => {
this.handlers.delete(handler);
};
},
reconnect() {
this.handlers.forEach((handler) => handler());
},
};
// Which theme each handler showed its notification in, on the last reconnect.
const shown = new Map<string, Theme>();
function showNotification(handler: string, theme: Theme) {
shown.set(handler, theme);
}
// Plain function, left out of the deps: the effect runs once, so the
// connection keeps the onConnected from the first render, and its theme.
function PlainFunction({ theme }: { theme: Theme }) {
const onConnected = () => {
showNotification("() => {}", theme);
};
useEffect(() => {
return connection.on("connected", () => onConnected());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
}
// useEffectEvent: the effect still runs once, but onConnected reads the
// latest theme every time it's called.
function WithEffectEvent({ theme }: { theme: Theme }) {
const onConnected = useEffectEvent(() => {
showNotification("useEffectEvent", theme);
});
useEffect(() => {
return connection.on("connected", () => onConnected());
}, []);
return null;
}
export function UseEffectEventDemo() {
const [theme, setTheme] = useState<Theme>("light");
const { log, logTable } = useLogger();
function toggleTheme() {
const next = theme === "light" ? "dark" : "light";
setTheme(next);
log("تم عوض شد:", next);
}
function reconnect() {
shown.clear();
connection.reconnect();
logTable(
[...shown].map(([handler, read]) => ({
handler,
"theme read": read,
"theme now": theme,
stale: read !== theme,
})),
);
}
return (
<div className="flex flex-col gap-4">
<PlainFunction theme={theme} />
<WithEffectEvent theme={theme} />
<div className="flex flex-wrap gap-2">
<Button variant="secondary" onClick={toggleTheme}>
تغییر تم
</Button>
<Button onClick={reconnect}>اتصال دوباره</Button>
</div>
<Typography variant="caption">
تم فعلی:{" "}
<Typography as="span" variant="mono" className="text-text">
{theme}
</Typography>
</Typography>
</div>
);
}
useEffectEvent و closure کهنه
§ useEffectEvent و closure کهنه
دو handler به یک connection ساختگی وصل شدهاند و effect هر دو فقط یک بار اجرا میشود: یکی تابع معمولی است که از deps بیرون مانده و دیگری useEffectEvent. «اتصال دوباره» را بزنید، تم را عوض کنید و دوباره وصل شوید؛ تابع معمولی هنوز تم اول را میخواند و useEffectEvent تم فعلی را.
پیشنمایش
تم فعلی: light
خروجی
هنوز چیزی ثبت نشده.
کد