これは、私が以前に提起した質問のより簡潔なバージョンです。うまくいけば、それはよりよく説明され、より理解しやすくなります。
これは、数字を期待する 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 : その場合、各入力コンポーネントで非同期処理が発生するため、実際のアプリで不要と思われる問題を回避するには、どのような設計を検討する必要がありますか?