React如何避免不必要重渲染引发的重复API调用问题
问题修复方案
核心根因
- 子组件异步逻辑错误:
setNewData是异步更新状态,await api.call()拿到结果后直接读取newData无法拿到最新返回值,会触发空指针报错,导致组件异常重挂载,形成无限循环 - 父组件每次重渲染都会生成新的
checkAvailability函数引用,触发子组件不必要的重渲染 - 多商品状态冲突:多个CartItem同时修改父级状态会出现结果覆盖问题,无法满足「只要有一个商品不可用就禁用按钮」的需求
- 逻辑倒置:按钮禁用逻辑和需求相反,当前代码是
available为true时禁用按钮,和需求「API返回false时禁用按钮」不符
修复后的代码
父组件Cart
import {useState, useCallback} from "react"; import CartItem from "./CartItem"; const Cart = ({cart}) => { // 定义为所有商品是否都可用,初始值为true const [allAvailable, setAllAvailable] = useState(true); // 用useCallback包裹,保持函数引用不变,避免子组件不必要重渲染 const checkAvailability = useCallback((itemAvailable) => { // 只要有一个商品不可用,就设为false,后续不用再修改 if (!itemAvailable) { setAllAvailable(false) } }, []) return ( <> {cart.items.map((item) => ( item.is_valid && <CartItem key={item.id} checkAvailability={checkAvailability} /> ))} {/* 需求:存在不可用商品时禁用按钮,即allAvailable为false时禁用 */} <button disabled={!allAvailable} >Click Me!</button> </> ) } export default Cart;
子组件CartItem
import {useEffect, memo} from "react"; // 用memo包裹,props不变时不会重渲染 const CartItem = memo(({checkAvailability}) => { const handleCheck = async () => { const data = await api.call(); // 直接用接口返回的data判断,不需要多余的state存储 checkAvailability(data.available) }; useEffect(() => { handleCheck(); }, [checkAvailability]); // 加入checkAvailability作为依赖,因为用了useCallback包裹,引用不会变 return ( <div> Item </div> ) }); export default CartItem;
额外优化说明
- 移除了子组件多余的
newData状态,减少不必要的状态更新开销 - 父级状态更新逻辑调整为只有当商品不可用时才修改,避免频繁更新状态
- 如果需要支持商品动态增删的场景,可以给父组件维护一个不可用商品ID的集合,删除商品时同步更新集合判断是否全部可用
内容的提问来源于stack exchange,提问作者Abdulrahman sabah
相关产品推荐
相关产品推荐

