如何在自定义React Hook中编写适配不同滚动目标的整洁条件逻辑?
滚动目标双适配的整洁实现方案
核心要解决window和DOM元素的垂直滚动位置获取API不统一问题,同时修正原有示例代码里的变量不匹配、依赖缺失、逻辑冗余问题。
具体优化点
- 封装统一的滚动取值工具函数,完全隔离两类目标的API差异,避免在事件回调里散落判断逻辑
- 修正原有代码的变量错误:原代码中引用的
scrollRef.current与实际定义的myRef不匹配,会直接触发未定义报错 - 收敛重复的状态赋值逻辑,去掉无意义的if-else分支,直接将阈值比较结果传入state更新函数
- 补全effect的依赖项,避免闭包带来的目标值读取异常
- 初始执行一次滚动回调,避免DOM挂载时初始滚动位置已经超过阈值、但状态不更新的问题
优化后完整代码
import { useState, useEffect, useRef } from 'react'; type ScrollTarget = 'ref' | 'window'; // 统一获取垂直滚动位置 const getScrollTopValue = (target: HTMLElement | Window): number => { if (target === window) { return window.pageYOffset; } return target.scrollTop; }; const useMyHook = (target: ScrollTarget) => { const [fade, setFade] = useState(false); const myRef = useRef<HTMLDivElement>(null); useEffect(() => { const scrollNode = target === 'ref' ? myRef.current : window; // 节点未挂载时跳过事件绑定 if (!scrollNode) return; const onScroll = () => { const currentTop = getScrollTopValue(scrollNode); setFade(currentTop > 50); }; // 初始化校验一次滚动位置 onScroll(); scrollNode.addEventListener('scroll', onScroll); return () => { scrollNode.removeEventListener('scroll', onScroll); }; }, [target]); return { myRef, fade }; }; export default useMyHook;
注意事项
- 因为ref的
current属性变化不会触发组件重渲染,当目标为ref类型时,要确保DOM节点已经挂载到ref上,否则effect执行时会因为拿不到节点跳过事件绑定。常规静态DOM场景下这个逻辑完全可用。 - 类型层面的收窄可以帮TypeScript正确识别两类目标的属性访问,避免TS报“scrollTop不存在于window、pageYOffset不存在于HTMLElement”的类型错误。
内容的提问来源于stack exchange,提问作者meez
相关产品推荐
相关产品推荐

