React Hook useSlideScroll在点击回调中调用触发规则报错,求助排查
问题原因与解决方案
核心问题
- 错误的Hook命名与定义:你写的
useSlideScroll根本不是符合React规范的自定义Hook——它没有调用任何React内置Hook(比如useState、useEffect),本质就是一个普通工具函数。但你给它加了use前缀,导致ESLint的react-hooks/rules-of-hooks规则将其判定为Hook,触发检查。 - 违反Hook调用规则:即便它是真的Hook,也绝对不能在
onClick这类事件回调里调用。React要求Hook必须在函数组件或自定义Hook的顶层作用域调用,不能嵌套在循环、条件判断、事件回调中,否则会破坏Hook的状态维护逻辑。
修复步骤
1. 将"伪Hook"改为普通工具函数
去掉use前缀,改成普通函数,还可以增加空值判断避免报错:
// hooks/slideScroll.js export const slideScroll = (element, speed, distance, step) => { if (!element) return; // 防止DOM元素未挂载的情况 let scrollAmount = 0; const slideTimer = setInterval(() => { element.scrollLeft += step; scrollAmount += Math.abs(step); if (scrollAmount >= distance) { clearInterval(slideTimer); } }, speed); return slideTimer; // 返回定时器ID,方便后续清理 };
2. 在组件中正确调用工具函数
增加定时器的清理逻辑,避免快速点击导致多个定时器同时运行,以及组件卸载时的内存泄漏:
import { useRef, useEffect } from 'react'; import { slideScroll } from '../hooks/slideScroll'; import Image from 'next/image'; // 假设你用的是Next.js的Image组件 const YourComponent = () => { const scrollRef = useRef(null); const timerRef = useRef(null); // 保存定时器ID,用于清理 const handlePrevClick = () => { // 先清理之前的定时器,避免冲突 if (timerRef.current) { clearInterval(timerRef.current); } // 调用工具函数 timerRef.current = slideScroll(scrollRef.current, 25, 100, -10); }; // 组件卸载时清理定时器 useEffect(() => { return () => { if (timerRef.current) { clearInterval(timerRef.current); } }; }, []); return ( <div ref={scrollRef} className="overflow-x-auto"> {/* 你的滚动内容 */} <Image src={'/images/prevArrow.svg'} alt="arrow left" height="10" width="10" className="cursor-pointer h-[10px] w-[10px]" onClick={handlePrevClick} /> </div> ); }; export default YourComponent;
补充说明
- React自定义Hook的核心是复用状态逻辑,必须依赖React内置Hook实现,且命名必须以
use开头。如果只是纯逻辑的工具函数,不需要用Hook的方式定义。 - 事件回调是用户交互时才执行的代码,而Hook需要在组件每次渲染时按固定顺序调用,所以绝对不能在回调里调用Hook。
内容的提问来源于stack exchange,提问作者Valentino Fabris Valenti
相关产品推荐
相关产品推荐

