React路由切换时useRef报Cannot read property 'style' of null错误如何解决
问题根因
useRef未指定类型与初始值,默认初始值为undefined,TypeScript无法确定其是否存在,因此抛出2532类型错误- 平滑滚动的
requestAnimationFrame循环未设置清理逻辑,路由切换组件卸载后,循环仍在执行,此时scrollingContainerRef.current已经被销毁为null,访问style属性就会抛出空指针错误 - 所有用到
scrollingContainerRef.current的逻辑未做存在性校验,ref未完成挂载时调用就会报错 - 滚动状态
data定义在组件函数体内,每次组件重渲染都会被重置为初始值,会导致滚动逻辑异常,属于隐藏bug
修复步骤
- 给
useRef指定HTMLDivElement类型,初始值设为null - 用
useRef存储滚动状态data与requestAnimationFrame的id,避免重渲染重置 - 所有访问
scrollingContainerRef.current的逻辑前置判空 - 在
useEffect的清理函数中取消requestAnimationFrame循环,组件卸载时停止滚动逻辑
修复后完整代码
const Scroll: React.FC = ({ children }) => { const windowSize = useWindowSize(); // 给ref指定类型和初始值 const scrollingContainerRef = useRef<HTMLDivElement>(null); // 用useRef存储滚动状态和raf id,避免重渲染被重置 const dataRef = useRef({ ease: 0.1, current: 0, previous: 0, rounded: 0, }); const rafIdRef = useRef<number>(); const setBodyHeight = () => { // 先判断ref存在再执行逻辑 if (isBrowser && scrollingContainerRef.current) { document.body.style.height = `${ scrollingContainerRef.current.getBoundingClientRect().height }px`; } }; useEffect(() => { setBodyHeight(); }, [windowSize.height]); const smoothScrollingHandler = () => { // 先判断ref存在再执行滚动逻辑 if (!scrollingContainerRef.current) return; const data = dataRef.current; data.current = isBrowser ? window.scrollY : 0; data.previous += (data.current - data.previous) * data.ease; data.rounded = Math.round(data.previous * 100) / 103; scrollingContainerRef.current.style.transform = `translateY(-${data.previous}px)`; rafIdRef.current = requestAnimationFrame(() => smoothScrollingHandler()); }; useEffect(() => { rafIdRef.current = requestAnimationFrame(() => smoothScrollingHandler()); // 组件卸载时清理raf循环 return () => { if (rafIdRef.current) { cancelAnimationFrame(rafIdRef.current); } }; }, []); return ( <Wrapper> <div ref={scrollingContainerRef}>{children}</div> </Wrapper> ); };
内容的提问来源于stack exchange,提问作者Darren
相关产品推荐
相关产品推荐

