React Native中useState状态更新失效 数组仅保存最后一个元素
问题根因
for循环内多次调用setData时,每次展开的data都是fetchNewData执行瞬间被闭包捕获的旧状态值——也就是循环启动时的初始空数组[],根本读不到前一次setData执行后的最新状态。
每次执行setData([...data, newUserData])本质都是往空数组里塞入当前遍历到的单条数据,前面的更新结果会被后续操作直接覆盖,最终状态只会保留最后一次遍历拿到的用户数据,监听data的useEffect自然每次都只能打印单条数据,达不到追加效果。
修复方案
方案1:函数式状态更新(适配边拉取边追加场景)
React的setState支持传入更新函数,函数入参固定为上一次更新完成后的最新状态,完全不受闭包旧值影响:
const fetchNewData = async allKeys => { for(let i=0;i<allKeys.length;i++){ let newUserData = // 单条数据接口拉取逻辑 // 基于前序最新状态做追加 setData(prevData => [...prevData, newUserData]) } }
该方案的缺点是每拉到一条数据就会触发一次组件重渲染,数据量大时性能开销较高。
方案2:批量拉取后一次性更新(推荐,性能最优)
先在循环里把所有用户数据拉取完成、组装为完整数组,再一次性调用setData做合并,全程只会触发一次组件重渲染:
const fetchNewData = async allKeys => { const newUserDataList = [] for(let i=0;i<allKeys.length;i++){ let newUserData = // 单条数据接口拉取逻辑 newUserDataList.push(newUserData) } // 一次性合并所有新老数据 setData(prevData => [...prevData, ...newUserDataList]) }
修复后完整参考代码
const Index=()=>{ const [data, setData] = useState([]); // 存储追加后的完整消息/用户列表 const [keys, setKeys] = useState([]); // 存储用户id集合 const fetchNewData = async allKeys => { const userDataList = [] for(let i=0;i<allKeys.length;i++){ let newUserData = // 接口获取单条用户数据逻辑 userDataList.push(newUserData) } setData(prevData => [...prevData, ...userDataList]) } // 监听Context.teacher变化拉取对应数据 useEffect(() => { let tempKeys = Context.teacher.map(item => item.id); fetchNewData(tempKeys); setKeys(tempKeys); }, [Context.teacher]) // 监听data变化做后续逻辑 useEffect(() => { console.log('data has been set: ', data); // 修复后将打印完整的追加后数组 }, [data]); return /* 业务JSX内容 */ } export default Index;
优化注意事项
- 无论用哪种更新逻辑,都优先推荐
setData(prevData => 新状态)的函数式更新写法,从根源上规避闭包旧值导致的更新异常。 - 如果后端接口支持批量传参查询,不要循环发起多次单条请求,直接传入所有id一次拉取全量数据,能大幅降低网络请求开销。
内容的提问来源于stack exchange,提问作者Faizan Muhammad
相关产品推荐
相关产品推荐

