如何创建React Hook跟踪页面滚动深度并在组件卸载时正确记录最大值?
解决React Hook跟踪滚动深度在组件卸载时获取正确值的问题
问题描述
我正在开发一个用于跟踪用户页面最大滚动深度的React Hook,希望在组件卸载时通过useEffect的清理函数记录这个最大值。目前Hook本身能正常工作,但在Home组件里使用时遇到以下问题:
- 若给useEffect的依赖数组添加
maxScrollDepth,滚动时会持续打印日志; - 若依赖数组为空,组件卸载时仅打印初始值0。
需要实现组件卸载时仅打印一次正确的最大滚动深度。
原代码
useTrackScrollDepth Hook
import { useEffect, useRef } from 'react'; export const useTrackScrollDepth = () => { const scrollDepthRef = useRef(0); const trackScrollDepth = () => { const scrolledValue = window.scrollY; scrollDepthRef.current = Math.max(scrolledValue, scrollDepthRef.current); }; useEffect(() => { const handleScrollTracking = () => { trackScrollDepth(); }; document.addEventListener('scroll', handleScrollTracking); return () => { document.removeEventListener('scroll', handleScrollTracking); }; }, []); return scrollDepthRef.current; };
Home组件使用代码
import React, { useEffect } from 'react'; import { useTrackScrollDepth } from 'hooks/useTrackScrollDepth'; export function Home() { const maxScrollDepth = useTrackScrollDepth(); useEffect( () => () => { console.log(`Max scroll depth: ${maxScrollDepth}`); }, [], ); return ( <div>Home placeholder</div> ); }
解决方案
1. 修改useTrackScrollDepth Hook,返回ref对象
直接返回scrollDepthRef.current会导致组件每次渲染拿到的是快照值,无法实时获取最新滚动深度。改为返回ref对象,让外部可以直接访问最新的current属性:
import { useEffect, useRef } from 'react'; export const useTrackScrollDepth = () => { const scrollDepthRef = useRef(0); const trackScrollDepth = () => { const scrolledValue = window.scrollY; scrollDepthRef.current = Math.max(scrolledValue, scrollDepthRef.current); }; useEffect(() => { const handleScrollTracking = () => { trackScrollDepth(); }; document.addEventListener('scroll', handleScrollTracking); return () => { document.removeEventListener('scroll', handleScrollTracking); }; }, []); // 返回ref对象,而非current值 return scrollDepthRef; };
2. 在Home组件中通过ref访问最新值
组件中拿到ref后,在卸载清理函数中直接读取ref.current,依赖数组保持为空即可确保仅在卸载时执行一次:
import React, { useEffect } from 'react'; import { useTrackScrollDepth } from 'hooks/useTrackScrollDepth'; export function Home() { const maxScrollDepthRef = useTrackScrollDepth(); useEffect( () => () => { console.log(`Max scroll depth: ${maxScrollDepthRef.current}`); }, [], // 依赖数组为空,仅在组件卸载时执行清理函数 ); return ( <div>Home placeholder</div> ); }
原理说明
- ref对象的引用在组件生命周期内不会改变,因此返回ref不会触发组件不必要的重渲染;
- 卸载时读取
ref.current,能直接获取到滚动过程中更新的最新最大滚动深度; - 依赖数组为空,确保清理函数仅在组件卸载时执行一次,避免滚动时重复打印日志。
内容的提问来源于stack exchange,提问作者jcarapia
相关产品推荐
相关产品推荐

