React中map函数内调用useState更新状态后读取值为空的问题
问题成因
React 函数组件的 state 更新采用异步批处理机制,setBrandName 调用后不会立刻修改当前执行上下文里的 brandName 变量,只是向 React 调度一次组件重渲染:
- 你在 onClick 回调里访问的
brandName,是当前渲染轮次被 JS 闭包捕获的固定值,初始值为空字符串,所以第一次点击时打印出来永远是空。 - 新设置的品牌名只会在下一轮组件重渲染完成后,才会成为新闭包里的
brandName值。 items.name是map遍历当前项直接拿到的瞬时值,不受 state 异步更新逻辑影响,所以可以正常打印。
修复方案
根据实际使用场景选择对应方案即可:
方案1:点击逻辑直接使用遍历拿到的当前项值
如果点击后需要立刻用选中的品牌名做后续逻辑(比如传参、发请求、做判断),不需要等 state 更新,直接用已经拿到的 items.name 即可,这也是性能最高的写法:
onClick={() => { // 注意:setBrandSelected 推荐用函数式更新,避免闭包导致的状态更新错误 setBrandSelected(prev => !prev); const currentBrand = items.name; // 所有需要用到当前选中品牌名的逻辑,直接用currentBrand即可 console.log("当前选中品牌:", currentBrand); setBrandName(currentBrand); }}
方案2:用useEffect监听state变化执行后续逻辑
如果逻辑必须要等 brandName 状态更新完成后再执行(比如多个状态联动的场景),不要在set调用的同步代码里写相关逻辑,把逻辑放到useEffect的监听回调里:
import { useState, useEffect } from 'react'; // 组件内状态定义 const brands = [ { key: 1, name: "Chanel" }, { key: 2, name: "louis Vuitton" }, { key: 3, name: "Gucci" }, { key: 4, name: "Dior" }, ]; const [brandName, setBrandName] = useState(""); const [brandSelected, setBrandSelected] = useState(false); // 监听brandName变化 useEffect(() => { // 这里拿到的永远是更新后的最新brandName console.log("状态更新完成,当前选中品牌:", brandName); // 依赖最新brandName的逻辑写在这里 }, [brandName]); // 列表渲染逻辑 {brands.map((item) => ( <ButtonBase onClick={() => { setBrandSelected(prev => !prev); setBrandName(item.name); }} key={item.key} sx={{ width: "100%", mt: "1%", height: "20%" }} > <MDBox display="flex" flexDirection="row" width="100%" height="100%" alignItems="center" justifyContent="flex-start" sx={{ backgroundColor: "#ffffff", "&:hover": { backgroundColor: "#f5ede9", }, pl: "4%", }} > <MDTypography>{item.name}</MDTypography> </MDBox> </ButtonBase> ))}
补充说明:所有依赖前一个状态值的set操作,都推荐用函数式更新写法(比如
setBrandSelected(prev => !prev)),避免React批处理多个状态更新时,闭包捕获的旧值导致更新结果不符合预期。
内容的提问来源于stack exchange,提问作者Zheng Jie
相关产品推荐
相关产品推荐

