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

React useState点击行后状态渲染滞后问题求助

问题:受控输入组件渲染滞后,展示上一次数据

使用受控输入组件时,将API响应传递给子组件<AdminInvociceDetail>,点击数据行后,子组件总是渲染上一次的数值。

传递数据行ID的父组件代码:

<AdminInvociceDetail row={invoiceRowClicked} />

子组件完整代码:

import React, { useState, useEffect } from "react";
import {
 useFetchInvoiceQuery,
 useUpdateInvoiceMutation,
} from "../../features/Invoices/InvoiceSlice";
import { useNavigate, Link } from "react-router-dom";
import "./Admin.css";

const AdminInvociceDetail = ({ row }) => {
  const { data } = useFetchInvoiceQuery(row);
  const [UpdateInvoice] = useUpdateInvoiceMutation();

  //* state for the form
  const [Id, setId] = useState("");
  const [billtoname, setBillToName] = useState("");
  const [clientname, setClientName] = useState("");
  const [offc, setOffc] = useState("");
  const [matterNo, setMatterNo] = useState("");

  useEffect(() => {
    setId(data?.ID);
    setBillToName(data?.BillToName);
    setOffc(data?.OFFC);
    setClientName(data?.ClientName);
    setMatterNo(data?.MatterNo);
  }, [row]);

  function ButtonLink({ to, children, className }) {
    return (
      <Link to={to}>
        <button className={className}>{children}</button>
      </Link>
    );
  }

  return (
    <div>
      <div class="box">
        <form class="ui form" onSubmit={handleSubmit}>
          <h4 class="ui dividing header labels">Invoice Detail:</h4>
          <div class="two fields">
            <div class="field">
              <label className="labels">Bill To Name</label>
              <input
                type="text"
                name="BillToName"
                placeholder="Bill To Name"
                required
                onChange={(e) => setBillToName(e.target.value)}
                value={billtoname || ""}
              />
            </div>
            <div class="field">
              <label className="labels">Client Location</label>
              <select
                class="ui fluid dropdown"
                required
                onChange={(e) => setClientName(e.target.value)}
                value={clientname || ""}
              >
                <option value="">Location</option>
                <option value="Becker - Tampa">Becker - Tampa</option>
                <option value="Becker - Orlando">Becker - Orlando</option>
                <option value="Becker - West Palm Beach">
                  Becker - West Palm Beach
                </option>
                <option value="Becker - Fort Myers">Becker - Fort Myers</option>
                <option value="Becker - Coral Gables">
                  Becker - Coral Gables
                </option>
                <option value="Becker - Fort Lauderdale">
                  Becker - Fort Lauderdale
                </option>
                <option value="Becker - Morristown">Becker - Morristown</option>
                <option value="Becker - Bradenton">Becker - Bradenton</option>
                <option value="Becker - Stuart">Becker - Stuart</option>
                <option value="Becker - Naples">Becker - Naples</option>
                <option value="Becker - Sarasota">Becker - Sarasota</option>
                <option value="Becker - Fort Walton Beach">
                  Becker - Fort Walton Beach
                </option>
                <option value="Becker - New York">Becker - New York</option>
                <option value="Becker - Mount Laurel">
                  Becker - Mount Laurel
                </option>
                <option value="Becker - Red Bank">Becker - Red Bank</option>
                <option value="Becker - Tallahassee">
                  Becker - Tallahassee
                </option>
              </select>
            </div>
          </div>
          <div class="two fields">
            <div class="field">
              <label className="labels">Office</label>
              <select
                class="ui fluid dropdown"
                required
                onChange={(e) => setOffc(e.target.value)}
                name="offc"
                value={offc || ""}
              >
                <option value="">Office</option>
                <option value="TAL">TAL</option>
                <option value="ORL">ORL</option>
                <option value="WPB">WPB</option>
                <option value="FTM">FTM</option>
                <option value="MIA">MIA</option>
                <option value="FTL">FTL</option>
                <option value="MOR">MOR</option>
                <option value="STR">STR</option>
                <option value="NAP">NAP</option>
                <option value="BDT">BDT</option>
                <option value="SAR">SAR</option>
                <option value="FTW">FTW</option>
                <option value="TAM">TAM</option>
                <option value="MTL">MTL</option>
                <option value="MOR">MOR</option>
                <option value="RB">RB</option>
                <option value="NY">NY</option>
                <option value="DC">DC</option>
              </select>
            </div>
            <div class="field">
              <label className="labels">Matter No</label>
              <input
                type="text"
                name="MatterNo"
                placeholder="Matter No"
                onChange={(e) => setMatterNo(e.target.value)}
                value={matterNo || ""}
              />
            </div>
          </div>
          <ButtonLink to="/" className="back-button">
            Back to Invoice
          </ButtonLink>
          <button class="submit-button">Submit Invoice</button>
          <div class="fields"></div>
        </form>
      </div>
    </div>
  );
}

问题原因:

  • useEffect的依赖数组设置为[row],当row变化时触发执行,但此时useFetchInvoiceQuery(row)返回的data仍为上一次请求的缓存结果(新API请求尚未完成),导致用旧数据更新表单状态,出现滞后。
  • 新的API数据返回后,useEffect未监听data变化,无法同步更新表单状态。

解决方案:

1. 修正useEffect依赖,监听data变化

将useEffect的依赖改为data,确保新API数据返回时立即同步表单状态:

useEffect(() => {
  if (data) { // 避免data为undefined时更新
    setId(data.ID);
    setBillToName(data.BillToName);
    setOffc(data.OFFC);
    setClientName(data.ClientName);
    setMatterNo(data.MatterNo);
  }
}, [data]);

2. (可选)直接使用API数据作为受控组件数据源

如果不需要保留用户编辑后的临时状态,可以跳过本地useState,直接用data作为输入框的value,减少同步问题:

<input
  type="text"
  name="BillToName"
  placeholder="Bill To Name"
  required
  onChange={(e) => setBillToName(e.target.value)}
  value={data?.BillToName || ""}
/>

注意:这种方式下,API数据更新会覆盖用户输入,适合仅展示或需要严格同步API数据的场景。

3. 确保RTK Query参数变化时重新请求

如果存在缓存导致的旧数据问题,调用useFetchInvoiceQuery时添加配置,强制参数变化时重新请求:

const { data } = useFetchInvoiceQuery(row, {
  refetchOnMountOrArgChange: true,
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:12:02