我有一个 Next.js 14 项目,在该项目中,我尽可能优先使用服务器组件而不是客户端组件。
布局是服务器渲染的,而其中使用的组件包装器是客户端渲染的。这些组件之一在页面上多次实例化,其内部工作的一部分涉及初始化
useBreakpoint
钩子的实例。
useBreakpoint
钩子的目的是根据屏幕尺寸计算并返回当前断点状态(两个字母的字符串)。
当我们在应用程序中有此钩子的多个实例时,每个实例都会在每个调整大小事件上计算当前断点。这导致时间复杂度为 O(n),其中 n 是初始化钩子的组件数量。
由于主要布局是服务器渲染的,因此像 React Context 或 Redux 这样的单例解决方案将无法工作。这就是为什么我想出了以下内容:
import { useEffect, useState } from 'react';
import { BreakpointKey, getBreakpointKey } from '@/lib/breakpoints';
const handleResize = () => {
const width = window.innerWidth;
const matchingBreakpoint = getBreakpointKey(width);
const event = new CustomEvent('breakpointChange', { detail: matchingBreakpoint });
window.dispatchEvent(event);
};
const useBreakpoint = () => {
const [breakpoint, setBreakpoint] = useState<BreakpointKey>('zr');
useEffect(() => {
const handleBreakpointChange = (event: CustomEvent<BreakpointKey>) => {
setBreakpoint(event.detail);
};
window.addEventListener('breakpointChange', handleBreakpointChange);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('breakpointChange', handleBreakpointChange);
};
}, []);
return breakpoint;
};
export default useBreakpoint;
所以基本上,我们附加了一个调整大小事件处理程序的实例,其唯一目的是触发一个自定义的 breakpointChange
事件,该事件保存计算断点的有效负载。因此,钩子的所有实例现在都可以注册自己的
breakpointChange
事件侦听器,而无需每个实例都执行相同的计算。因此,从计算断点的 O(n) 开始,该解决方案将其减少到 O(1)。我在简短的研究中没有遇到过这种方法,所以我想在这里分享它以征求您的反馈。有什么我可能忽略的缺点吗?例如,是否值得为此目的创建一个全新的活动?谢谢!
N 组件调用 useBreakpoint
钩子,该钩子实例化了 "resize"
侦听器
和
"breakpointChange"
事件侦听器,所以N
useBreakpoint
挂钩调用最终会触发N
handleResize
和
handleBreakpointChange
调用。如果与仅在
O(c * n)
事件处理程序中完成工作相比,这已经创建了 更多 工作(尽管数量恒定,因此根据您的计算,
c * O(n)
或
"resize"
)。此外,useBreakpoint
钩子忽略清理 "resize"
侦听器。建议重构删除
"breakpointChange"
"resize"
事件映射到 "breakpointChange"
事件。正确维护事件监听器,包括在组件卸载时删除它们。handleResize
移至
useEffect
钩子回调主体,这样它就不是外部依赖项。
import { useEffect, useState } from 'react';
import { BreakpointKey, getBreakpointKey } from '@/lib/breakpoints';
const useBreakpoint = () => {
const [breakpoint, setBreakpoint] = useState<BreakpointKey>('zr');
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
const matchingBreakpoint = getBreakpointKey(width);
setBreakpoint(matchingBreakpoint);
// or setBreakpoint(getBreakpointKey(window.innerWidth));
};
window.addEventListener('resize', handleResize);
// Invoke once to set state for initial window width
handleResize();
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return breakpoint;
};
export default useBreakpoint;