You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React父组件状态被子组件修改后不重新渲染问题排查

问题描述

父组件拥有本地状态,将setAdded函数传递给子组件ExtraItem。子组件调用该函数能修改父组件的added状态,但父组件不会触发重新渲染;只有在父组件内部修改状态时,才会触发重渲染,此时能看到子组件的修改结果,说明子组件确实修改了父状态,但子组件触发的状态变更无法触发父组件重渲染。此外,父组件中依赖added的useEffect在子组件触发状态变更时也不会执行。

父组件代码
const CustomizeItem = ({ item, i }) => {
  const [removed, setRemoved] = useState([]);
  const [added, setAdded] = useState([]);
  const [price, setPrice] = useState(item.price);

  const handleRemovable = (removable) => {
    if (removed.indexOf(removable) === -1) {
      setRemoved([...removed, removable]);
    } else {
      let removedCopy = [...removed];
      removedCopy.splice(removed.indexOf(removable), 1);
      setRemoved(removedCopy);
    }
  };

  useEffect(() => {
    console.log(added);
    console.log(price);
  }, [added, price]);

  return (
    <div className="border-2 ">
      <p>
        Personaliza la {item.name} Nro {i + 1}:
      </p>
      <div>
        <p>Remover:</p>
        {item.removables.map((r, i) => {
          return (
            <label>
              {r.name}
              <input
                name={r.name}
                type="checkbox"
                checked={null}
                onChange={() => handleRemovable(r)}
              />
            </label>
          );
        })}
      </div>
      <div className="flex flex-col">
        <p>Añadir:</p>
        {item.extras.map((extra, i) => {
          return (
            <ExtraItem
              key={i + 1}
              extra={extra}
              added={added}
              setAdded={setAdded}
              removed={removed}
            />
          );
        })}
      </div>
      <div>
        <p>Resumen: </p>
        <div>
          {g(item, "Un", "Una", "s")} <strong>"{item.name}"</strong>
          {removed.length > 0 && (
            <p>
              {removed.map((r) => (
                <p>✖️ Sin {r.name}</p>
              ))}
            </p>
          )}
          {added.length > 0 && (
            <p>
              {added.map((a) => (
                <p>✔️ Con extra de {a.name}</p>
              ))}
            </p>
          )}
        </div>
      </div>
      <p>Precio: ${price}</p>
      <button></button>
    </div>
  );
};

export default CustomizeItem;
子组件代码
const ExtraItem = ({ added, setAdded, extra }) => {
  const [count, setCount] = useState(0);

  const addExtra = (extra) => {
    let addedCopy = added;
    addedCopy.push(extra);
    setAdded(addedCopy);
    setCount(count + 1);
  };

  const removeExtra = (extra) => {
    let addedCopy = added;
    addedCopy.splice(addedCopy.indexOf(extra), 1);
    setAdded(addedCopy);
    setCount(count - 1);
  };

  return (
    <div className="flex justify-between">
      <p>
        {extra.name} - ${extra.price}
      </p>
      <div className="flex gap-2">
        <IconButton
          variant="outlined"
          color="red"
          className="flex-none"
          size="sm"
          onClick={() => removeExtra(extra)}
        >
          <i class="fa-sharp fa-solid fa-minus" />
        </IconButton>
        <Typography variant="h4" className="text-justify text-green-400">
          {count}
        </Typography>
        <IconButton
          variant="outlined"
          color="green"
          className="flex-none"
          onClick={() => addExtra(extra)}
          size="sm"
        >
          <i className="fas fa-plus" />
        </IconButton>
      </div>
    </div>
  );
};

export default ExtraItem;
问题原因

React的状态更新机制依赖引用变化判断状态是否改变。子组件中let addedCopy = added是直接赋值数组引用,并未创建新数组,后续的push或splice都是修改原数组内容。调用setAdded(addedCopy)时传递的是同一个数组引用,React会判定状态未发生变化,因此不会触发父组件重渲染,依赖added的useEffect也不会执行。

修复方案

修改子组件的addExtra和removeExtra函数,创建原数组的新副本(生成新引用),再修改副本并传递给setAdded:

const ExtraItem = ({ added, setAdded, extra }) => {
  const [count, setCount] = useState(0);

  const addExtra = (extra) => {
    // 创建新数组副本
    const addedCopy = [...added];
    addedCopy.push(extra);
    setAdded(addedCopy);
    setCount(prev => prev + 1); // 函数式更新避免闭包问题
  };

  const removeExtra = (extra) => {
    // 生成过滤后的新数组
    const addedCopy = added.filter(item => item !== extra);
    setAdded(addedCopy);
    setCount(prev => prev - 1); // 函数式更新
  };

  return (
    <div className="flex justify-between">
      <p>
        {extra.name} - ${extra.price}
      </p>
      <div className="flex gap-2">
        <IconButton
          variant="outlined"
          color="red"
          className="flex-none"
          size="sm"
          onClick={() => removeExtra(extra)}
        >
          <i class="fa-sharp fa-solid fa-minus" />
        </IconButton>
        <Typography variant="h4" className="text-justify text-green-400">
          {count}
        </Typography>
        <IconButton
          variant="outlined"
          color="green"
          className="flex-none"
          onClick={() => addExtra(extra)}
          size="sm"
        >
          <i className="fas fa-plus" />
        </IconButton>
      </div>
    </div>
  );
};

export default ExtraItem;

更新count时使用函数式更新(setCount(prev => prev + 1)),可以避免因闭包导致的计数不准确问题。

内容的提问来源于stack exchange,提问作者Angel Orozco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:45:28