React:从受控子输入字段更新大型父记录时如何保持性能?

scr*_*ier 5 javascript reactjs

想象一个允许用户编辑账单(发票)的 React 页面。账单有相当多的可编辑字段,比如 100 个。此外,账单可以有任意数量的行项目。让我们想象一下其中的 100 个,每个都有,比如说,20 个字段。

如果帐单是“父项”,并且由 React 组件呈现,则行项目是“子项”,每个项都由“子”组件呈现。

我在父组件中维护帐单(及其行项目)的状态,感觉不错。

我想在子(行项目)组件中使用受控输入字段。然而,这意味着,至少天真地,即使我明智地使用 React.memo,每次按键都会导致父(账单)组件中的状态更新,以及一些重新渲染。

性能很快变得令人无法接受,即使只有 10 个订单项,例如每次按键延迟 500-800 毫秒。我已经做了一些性能调查,并且有几个可能的答案,但我不打算在这里报告它们,以免阻碍通往最佳答案的任何途径。

必须有一个通用的解决方案,不是吗?我想在没有表单库的情况下这样做,尽管我并不完全反对。

下面的精简示例,只是为了演示基本架构。该示例没有性能问题。:-)

帮助!我缺少保持这种性能的魔力是什么?


const INITIAL_STATE = {
  invoiceNumber: "ABC123",
  customer: "Phil's Dills",
  lineItems: [
    { index: 0, item: "Pickles", quantity: 2 },
    { index: 1, item: "Pickle Juice", quantity: 5 },
  ]
}

export function Bill() {
  const [ bill, setBill ] = useState(INITIAL_STATE);

  function updateBill(updatedLineItem) {
    const newLineItems = [...bill.lineItems];
    newLineItems[updatedLineItem.index] = updatedLineItem;
    setBill({
      ...bill,
      lineItems: newLineItems
    })
  }

  return(
    <div id="parent">
    <h1>Bill {bill.invoiceNumber} for {bill.customer}</h1>
    {bill.lineItems.map((lineItem) => (
      <LineItem key={lineItem.index} line={lineItem} updateBill={updateBill} />
    ))}
    </div>
    );
}

function LineItem({ line, updateBill }) {
  function updateField(e) {
    updateBill({
      ...line,
      [e.target.id]: e.target.value
    }); 
  }

  return(
    <div id="child">
      <input id="quantity" value={line.quantity} onChange={updateField} />
      <input id="item" value={line.item} onChange={updateField} />
    </div>
    );
}```
Run Code Online (Sandbox Code Playgroud)

Nic*_*ick 0

我认为您的问题实际上是,updateBill每次该组件重新渲染时您都会重新创建,这意味着每个子组件也会重新渲染,因为它们都updateBill作为道具接收。考虑使用useCallback来记忆该updateBill函数:

const updateBill = useCallback(updatedLineItem => {
  setBill(bill => {
    const newLineItems = [...bill.lineItems];
    newLineItems[updatedLineItem.index] = updatedLineItem;
    return {
      ...bill,
      lineItems: newLineItems
    }
  })
}, []);
Run Code Online (Sandbox Code Playgroud)