React中使用无框架依赖循环工具处理本地及远程数组状态异常问题
问题根因
你推测的问题方向完全正确,异常是由以下几个原因共同导致的:
- 每次组件重渲染都会重新执行
cycleThrough,生成全新的迭代器实例,之前的索引状态完全丢失,所以每次点击后的取值逻辑都是基于全新的实例,结果自然乱序 - 初始状态下
remoteCount为null,直接传入cycleThrough会导致访问iterable.length时报错,这也是异常的原因之一 - 第二个代码片段里事件处理逻辑写反了:左切调用了
getNextRemoteCount,右切调用了getPrevRemoteCount,和预期的遍历方向相反
解决方案
用useMemo缓存cycleThrough的实例,只在远程数组加载完成、数组本身发生变化时才重建迭代器,同时修复调用逻辑的错误,完整可运行代码如下:
import { useState, useEffect, useMemo } from 'react'; const fetchArray = () => new Promise((resolve) => setTimeout(() => resolve(["1", "2", "3"]), 2000)); // 原工具函数逻辑不变,新增初始值暴露方便初始化 function cycleThrough(iterable, { startIndex = 0 } = {}) { let index = startIndex; const mod = (n, r) => ((n % r) + r) % r; const iterator = (function* () { while (true) { const dir = yield iterable[index]; index = mod(index + dir, iterable.length); } })(); // 启动generator iterator.next(); return { getPrev: () => iterator.next(-1).value, getNext: () => iterator.next(1).value, initialValue: iterable[startIndex] }; } export default function App() { const [remoteCount, setRemoteCount] = useState(null); const [remoteCountToDisplay, setRemoteCountToDisplay] = useState(null); // 缓存迭代器实例,仅当remoteCount变化时重建 const cycleInstance = useMemo(() => { if (!remoteCount) return {}; return cycleThrough(remoteCount, { startIndex: 1 }); }, [remoteCount]); useEffect(() => { fetchArray().then((array) => { setRemoteCount(array); setRemoteCountToDisplay(array[1]); }); }, []); const onLeftRemote = () => { if (!cycleInstance.getPrev) return; setRemoteCountToDisplay(cycleInstance.getPrev()); }; const onRightRemote = () => { if (!cycleInstance.getNext) return; setRemoteCountToDisplay(cycleInstance.getNext()); }; return ( <div className="App"> <h1>{remoteCount ? remoteCountToDisplay : "loading"}</h1> <button onClick={onLeftRemote}>leftRemote</button> <button onClick={onRightRemote}>rightRemote</button> </div> ); }
补充说明
- 你的工具函数写法不属于反模式,只需要注意在React组件中,持有内部可变状态的实例(比如这个带索引的迭代器),不要在渲染顶层直接初始化,必须用
useMemo/useRef缓存,避免重渲染时丢失状态 - 工具函数命名建议:如果你觉得
cycleThrough不够清晰,可以考虑createCircularTraverser、circularArrayIterator,表意更直接
内容的提问来源于stack exchange,提问作者Joji
相关产品推荐
相关产品推荐

