
优化
让页面兼容 Portal
当页面被嵌入到其他环境中时,情况会有些不同。
以下内容来自这篇文章。
一个带有键盘快捷键的主题提供者——按 Cmd+D 切换深色模式:
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
useEffect(() => {
const toggle = (e) => {
if (e.metaKey && e.key === "d") {
e.preventDefault();
setTheme((t) => (t === "dark" ? "light" : "dark"));
}
};
window.addEventListener("keydown", toggle);
return () => window.removeEventListener("keydown", toggle);
}, []);
return <div className={theme}>{children}</div>;
}但如果有人将应用渲染在弹出窗口、iframe 或通过 createPortal 呈现时,该快捷键便会失效。因为监听器被绑定在父窗口上,而不是你的组件所在的窗口。此时应使用 ownerDocument.defaultView:
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const ref = useRef(null);
useEffect(() => {
const win = ref.current?.ownerDocument.defaultView || window;
const toggle = (e) => {
if (e.metaKey && e.key === "d") {
e.preventDefault();
setTheme((t) => (t === "dark" ? "light" : "dark"));
}
};
win.addEventListener("keydown", toggle);
return () => win.removeEventListener("keydown", toggle);
}, []);
return (
<div ref={ref} className={theme}>
{children}
</div>
);
}现在,无论在哪种窗口环境中,该快捷键都能正常工作。