React:点击元素修改状态后触发函数组件执行并发起AJAX请求的实现问题
问题根源
- React状态更新依赖引用变化触发重渲染,你直接修改原有Map对象的内容、再把同一个引用的Map传给
setParentItems,React检测不到状态变更,所以不会触发重渲染,自然不会替换组件。 - 直接修改状态中的
item对象属性childItemsClicked属于直接修改状态对象,不符合React不可变数据的规范要求。
修复方案
1. 修正状态更新逻辑,保证返回新的Map实例
修改loadItem函数,做不可变更新,还可以用函数式更新优化useCallback依赖:
const loadItem = useCallback(item => { // 用setState函数式写法,不用依赖外部的parentItems setParentItems(prevParentItems => { // 基于旧Map创建新的Map实例,引用变化才会触发重渲染 const newParentItems = new Map(prevParentItems); // 创建新的item对象,不直接修改原状态里的item const updatedItem = { ...item, childItemsClicked: true }; newParentItems.set(item.id, updatedItem); return newParentItems; }); }, []); // 依赖设为空数组,避免重复生成函数,性能更好
2. 修复遍历和初始化的问题
- 给遍历的元素添加唯一
key属性,保证React可以正确跟踪元素变更 - 初始状态不要设为
null,避免第一次渲染调用parentItems.keys()报错
// 初始值设为空Map const [parentItems, setParentItems] = useState(new Map()); // 渲染部分补上key {[...parentItems.keys()].map(itemKey => { const currentItem = parentItems.get(itemKey); return ( <Element key={itemKey} onClick={() => loadItem(currentItem)} > <Item item={currentItem} /> </Element> ) })}
3. 可选优化:把Item组件挪到父组件外部
避免每次父组件重渲染都重新创建组件实例,导致不必要的卸载重挂载:
// 移到父组件外定义 const Item = ({item}) => { if (item.childItemsClicked) { return <ChildComponentItem item={item} />; } else { return <div />; } };
额外检查点
确认ChildComponentItem中的AJAX请求是写在空依赖的useEffect中,保证组件挂载时会自动触发请求:
const ChildComponentItem = ({item}) => { useEffect(() => { // 这里写你的AJAX请求逻辑 }, []) // 剩余组件逻辑 }
内容的提问来源于stack exchange,提问作者jenn hi
相关产品推荐
相关产品推荐

