2

これは、私が以前に提起した質問のより簡潔なバージョンです。うまくいけば、それはよりよく説明され、より理解しやすくなります。

これは、数字を期待する 3 つの入力を持つ小さなアプリです (数字以外も入力で​​きることは無視してください。それは重要ではありません)。表示されているすべての数値の合計を計算します。入力の 1 つを別の数値に変更すると、合計が更新されます。

アプリのキャプチャ

そのコードは次のとおりです。

import { useCallback, useEffect, useState } from 'react';

function App() {

  const [items, setItems] = useState([
    { index: 0, value: "1" },
    { index: 1, value: "2" },
    { index: 2, value: "3" },
  ]);

  const callback = useCallback((item) => {
    let newItems = [...items];
    newItems[item.index] = item;
    setItems(newItems);
  }, [items]);

  return (
    <div>
      <SumItems items={items} />
      <ul>
        {items.map((item) =>
          <ListItem key={item.index} item={item} callback={callback} />
        )}
      </ul>
    </div>
  );
}


function ListItem(props) {

  const [item, setItem] = useState(props.item);

  useEffect(() => {
    console.log("ListItem ", item.index, " mounting");
  })

  useEffect(() => {
    return () => console.log("ListItem ", item.index, " unmounting");
  });

  useEffect(() => {
    console.log("ListItem ", item.index, " updated");
  }, [item]);

  const onInputChange = (event) => {
    const newItem = { ...item, value: event.target.value };
    setItem(newItem);
    props.callback(newItem);
  }

  return (
    <div>
      <input type="text" value={item.value} onChange={onInputChange} />
    </div>);
};

function SumItems(props) {
  return (
    <div>Sum : {props.items.reduce((total, item) => total + parseInt(item.value), 0)}</div>
  )

}

export default App;

そして、これは起動時と 2 番目の入力を 2 から 4 に変更した後のコンソール出力です。

ListItem  0  mounting App.js:35
ListItem  0  updated App.js:43
ListItem  1  mounting App.js:35
ListItem  1  updated App.js:43
ListItem  2  mounting App.js:35
ListItem  2  updated App.js:43
ListItem  0  unmounting react_devtools_backend.js:4049:25
ListItem  1  unmounting react_devtools_backend.js:4049:25
ListItem  2  unmounting react_devtools_backend.js:4049:25
ListItem  0  mounting react_devtools_backend.js:4049:25
ListItem  1  mounting react_devtools_backend.js:4049:25
ListItem  1  updated react_devtools_backend.js:4049:25
ListItem  2  mounting react_devtools_backend.js:4049:25

ご覧のとおり、単一の入力が更新されると、すべての子は再レンダリングされず、最初にアンマウントされてから再マウントされます。すべての入力はすでに正しい状態にあり、更新する必要があるのは合計だけです。そして、それらの入力が何百もあると想像してみてください。

再レンダリングだけの問題であれば、メモ化を見ることができます。callbackしかし、変更されたために正確に更新されるため、それは機能しませんitems。いいえ、私の質問はすべての子のアンマウントについてです。

質問 1 : アンマウントを回避できますか?

Kent C. Dodds によるこの記事を信頼する場合、答えは単純にノーです (強調は私のものです)。

React の key prop を使用すると、コンポーネント インスタンスを制御できます。React がコンポーネントをレンダリングするたびに、関数を呼び出して、DOM の更新に使用する新しい React 要素を取得します。同じ要素タイプを返す場合、すべての* props が変更されたとしても、それらのコンポーネント/DOM ノードは保持されます。

(...)

これに対する例外は、 key propです。これにより、まったく同じ要素タイプを返すことができますが、React は以前のインスタンスを強制的にアンマウントし、新しいインスタンスをマウントします。これは、その時点でコンポーネントに存在していたすべての状態が完全に削除され、コンポーネントがすべての意図と目的のために「再初期化」されることを意味します。

質問 2 : その場合、各入力コンポーネントで非同期処理が発生するため、実際のアプリで不要と思われる問題を回避するには、どのような設計を検討する必要がありますか?

4

2 に答える 2