【问题标题】:React setState don't update the UI or triggers useEffectReact setState 不更新 UI 或触发 useEffect
【发布时间】:2025-12-20 12:45:11
【问题描述】:

所以我的包装器组件中有一个初始状态,其中包含两个项目

 const initialData = {
    first: clubData.applications[0].invoice_url,
    second: clubData.applications[0].invoice_url_2,
  };

  const [invoiceFiles, setInvoiceFiles] = useState(initialData);

,该组件有 2 个子组件,每个子组件获取其中一项作为文件道具,并且还可以根据它们使用的状态更改状态。

  <AddInvoice
          admin={admin}
          clubData={clubData}
          addFile={(file) => addFile("first", file)}
          file={invoiceFiles.first}
          deleteFile={() => deleteFile("first")}
          invoiceUrl={clubData.applications[0].invoice_url}
        />

当我从 AddInvoice 组件调用 addFile 函数时,包装器组件中没有任何反应,甚至没有调用 useEffect 函数。有人知道为什么会这样吗?

这是我的完整包装代码:

const AddInvoiceWrapper = ({ clubData, admin }) => {
  const initialData = {
    first: clubData.applications[0].invoice_url,
    second: clubData.applications[0].invoice_url_2,
  };

  const [invoiceFiles, setInvoiceFiles] = useState(initialData);

  const addFile = (index, file) => {
    let newFiles = invoiceFiles;
    newFiles[index] = file;
    console.log(newFiles);
    setInvoiceFiles(newFiles);
  };

  const deleteFile = (index) => {
    let newFiles = invoiceFiles;
    newFiles[index] = null;
    setInvoiceFiles(newFiles);
  };

  useEffect(() => {
    console.log("invoice files changed!");
  }, [invoiceFiles]);

  return (
    <Row>
      <Column>
        <Paragraph>Dodaj fakturę (dev) </Paragraph>
        <AddInvoice
          admin={admin}
          clubData={clubData}
          addFile={(file) => addFile("first", file)}
          file={invoiceFiles.first}
          deleteFile={() => deleteFile("first")}
          invoiceUrl={clubData.applications[0].invoice_url}
        />
      </Column>
      {invoiceFiles.first != null ? (
        <Column>
          <Paragraph>Dodaj koretkę faktury</Paragraph>
          <AddInvoice
            admin={admin}
            clubData={clubData}
            addFile={(file) => addFile("second", file)}
            file={invoiceFiles.second}
            deleteFile={() => deleteFile("second")}
            invoiceUrl={clubData.applications[0].invoice_url_2}
          />
        </Column>
      ) : null}
    </Row>
  );
};

export default AddInvoiceWrapper;

这也是子组件中触发包装器功能的功能:

 const handleChange = (e) => {
    e.preventDefault();
    addFile(e.target.files[0]);
  };

数据传递没有问题,在 addFile 函数中我得到了正确的新对象,但是当我使用“setInvoiceFiles”时没有任何反应,只有当我对代码进行一些更改并且热重载自动运行时,我才会看到更改刷新状态。

【问题讨论】:

    标签: reactjs react-hooks next.js


    【解决方案1】:

    当您像这样创建newFiles 时:let newFiles = invoiceFiles; newFiles 将具有与 invoiceFiles 相同的引用。当 React 比较对象时,它会比较它们的引用,因为您的 newFilesinvoiceFiles 具有相同的引用(即使内部值不同),React 不会将其标记为更新,因此不会触发重新渲染或调用 useEffect

    解决方案很简单:只需将我提到的行更改为:

    let newFiles = {...invoiceFiles};
    

    这一次,newFiles 将是一个具有不同引用的全新变量。

    【讨论】:

      【解决方案2】:

      你需要返回新数组来更新状态:

      const addFile = (index, file) => {
        setInvoiceFiles((preState) =>
          preState.map((item, i) => {
            return index === i ? file : item;
          }),
        );
      };
      

      【讨论】:

        【解决方案3】:

        由于指针相同,React 不会认为这是一个新数据,也不会重新渲染。试试 setInvoiceFiles([...newFiles]);

        您可以在 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax 阅读有关扩展运算符 (...) 的信息

        【讨论】: