ReactJS中API数据返回后调用setUsers更新状态异常:循环中仅单个用户被添加至状态数组
问题原因与解决方案
这个问题我之前也碰到过,核心原因是React状态更新的异步特性加上闭包捕获的旧状态值导致的。
为什么会只保留一个用户?
你在for循环里每次调用setUsers([...users, user])时,这里的users变量捕获的是组件初始渲染时的空数组([])——因为React的状态更新不会立即改变当前作用域里的users变量,而是会在下一次渲染时更新。所以每次循环执行的都是:
setUsers([...[], user]) // 等价于 setUsers([user])
最后一次循环的user会覆盖之前的状态,所以最终users里只有最后一个用户。
两种解决方案
方案1:使用函数式更新推荐
React的setState支持传入一个函数,这个函数会接收最新的状态值作为参数,这样就能确保每次更新都是基于最新的状态:
const [users, setUsers] = useState([]) try { const doc = await getDocument(searchObj) if (doc.status === 200) { for (let course of doc.data.course) { const user = await getUser(course.document.userId) if (user.status === 200) { // 使用函数式更新,基于前一次的状态追加用户 setUsers(prevUsers => [...prevUsers, user]) } } } } catch (error) { console.log(error) }
方案2:先收集所有用户再一次性更新更高效
如果你的用户请求之间没有依赖关系,还可以先把所有用户数据收集到一个临时数组里,最后再一次性更新状态,这样能减少不必要的组件重渲染:
const [users, setUsers] = useState([]) try { const doc = await getDocument(searchObj) if (doc.status === 200) { const fetchedUsers = [] for (let course of doc.data.course) { const user = await getUser(course.document.userId) if (user.status === 200) { fetchedUsers.push(user) } } // 一次性更新状态 setUsers(fetchedUsers) } } catch (error) { console.log(error) }
如果想进一步优化,还可以用Promise.all并行请求所有用户(前提是接口支持并行调用),这样能减少等待时间:
const [users, setUsers] = useState([]) try { const doc = await getDocument(searchObj) if (doc.status === 200) { // 并行发起所有用户请求 const userPromises = doc.data.course.map(course => getUser(course.document.userId) ) const userResponses = await Promise.all(userPromises) // 过滤出成功的用户数据 const fetchedUsers = userResponses.filter(res => res.status === 200) setUsers(fetchedUsers) } } catch (error) { console.log(error) }
内容的提问来源于stack exchange,提问作者Ciprian
相关产品推荐
相关产品推荐

