如何在useEffect外部访问其内部定义的IScroll实例实现按钮控制滚动?
解决方案
你可以通过useRef存储IScroll实例解决作用域访问问题,这是React中存储不需要触发重渲染的可变值的标准方案,你之前用state报错是因为state更新为异步逻辑,且state变更会触发额外重渲染,不适合存储第三方类实例。
修改后的完整代码如下:
function Arrangement() { const ref = useRef(null); // 新增ref存储iscroll实例 const iscrollRef = useRef(null); const [prevButton, setPrevButton] = useState(false); const [nextButton, setNextButton] = useState(true); useEffect(() => { const iscroll = new IScroll(ref.current, { keyBindings: true, scrollX: true, mouseWheel: true, click: true }); // 将实例挂载到ref上 iscrollRef.current = iscroll; iscroll.on("scrollEnd", () => { if (iscroll.x === 0) { setPrevButton(false); } else { setPrevButton(true); } if (iscroll.x === iscroll.maxScrollX) { setNextButton(false); } else { setNextButton(true); } }); return () => { iscroll.destroy(); // 卸载时清空ref避免内存泄漏 iscrollRef.current = null; } }, []); const onPrevButtonHandler = () => { // 先判断实例是否存在,避免初始化未完成时报错 if (!iscrollRef.current) return; iscrollRef.current.scrollBy(220, 0, 600); } const onNextButtonHandler = () => { if (!iscrollRef.current) return; iscrollRef.current.scrollBy(-220, 0, 600); } return ( <div className={classes.Container}> <button className={clsx(classes.Button, classes.ButtonLeft, { [classes.Button__active]: prevButton })} onClick={onPrevButtonHandler} type="button" aria-label="Prev button"> <ChevronLeft /> </button> <div className={classes.Wrapper} ref={ref}> <ul> <li></li> </ul> </div> <button className={clsx(classes.Button, classes.ButtonRight, { [classes.Button__active]: nextButton })} onClick={onNextButtonHandler} type="button" aria-label="Next button"> <ChevronRight /> </button> </div> ); } export default Arrangement;
核心说明
- useRef的
current属性读写都是同步操作,不会触发组件重渲染,完美适配存储第三方库实例的场景 - 点击事件中新增的非空判断可以避免IScroll未初始化完成时用户点击按钮抛出异常
- 清理函数中新增的清空ref操作可以避免组件卸载后出现野指针导致的内存泄漏问题
内容的提问来源于stack exchange,提问作者Emil Jamel Mahmuda
相关产品推荐
相关产品推荐

