[英]Prevent rerender on function prop update
我有一個包含多層子組件的表單。 表單的狀態保持在最高級別,我將函數作為道具傳遞以更新頂級。 唯一的問題是當表單變得非常大(您可以動態添加問題)時,每個組件都會在其中一個更新時重新加載。 這是我的代碼的簡化版本(或代碼和框: https ://codesandbox.io/s/636xwz3rr):
const App = () => {
return <Form />;
}
const initialForm = {
id: 1,
sections: [
{
ordinal: 1,
name: "Section Number One",
questions: [
{ ordinal: 1, text: "Who?", response: "" },
{ ordinal: 2, text: "What?", response: "" },
{ ordinal: 3, text: "Where?", response: "" }
]
},
{
ordinal: 2,
name: "Numero dos",
questions: [
{ ordinal: 1, text: "Who?", response: "" },
{ ordinal: 2, text: "What?", response: "" },
{ ordinal: 3, text: "Where?", response: "" }
]
}
]
};
const Form = () => {
const [form, setForm] = useState(initialForm);
const updateSection = (idx, value) => {
const { sections } = form;
sections[idx] = value;
setForm({ ...form, sections });
};
return (
<>
{form.sections.map((section, idx) => (
<Section
key={section.ordinal}
section={section}
updateSection={value => updateSection(idx, value)}
/>
))}
</>
);
};
const Section = props => {
const { section, updateSection } = props;
const updateQuestion = (idx, value) => {
const { questions } = section;
questions[idx] = value;
updateSection({ ...section, questions });
};
console.log(`Rendered section "${section.name}"`);
return (
<>
<div style={{ fontSize: 18, fontWeight: "bold", margin: "24px 0" }}>
Section name:
<input
type="text"
value={section.name}
onChange={e => updateSection({ ...section, name: e.target.value })}
/>
</div>
<div style={{ marginLeft: 36 }}>
{section.questions.map((question, idx) => (
<Question
key={question.ordinal}
question={question}
updateQuestion={v => updateQuestion(idx, v)}
/>
))}
</div>
</>
);
};
const Question = props => {
const { question, updateQuestion } = props;
console.log(`Rendered question #${question.ordinal}`);
return (
<>
<div>{question.text}</div>
<input
type="text"
value={question.response}
onChange={e =>
updateQuestion({ ...question, response: e.target.value })
}
/>
</>
);
};
我試過使用 useMemo 和 useCallback,但我不知道如何使它工作。 問題是傳遞函數以更新其父級。 每次表單更新時,如果不更新它,我無法弄清楚如何做到這一點。
我無法在任何地方在線找到解決方案。 也許我正在尋找錯誤的東西。 感謝您提供的任何幫助!
使用Andrii-Golubenko的答案和這篇文章React Optimizations with React.memo、useCallback 和 useReducer,我想出了這個解決方案: https : //codesandbox.io/s/myrjqrjm18
請注意控制台日志如何僅顯示已更改組件的重新渲染。
<Section
...
updateSection={value => updateSection(idx, value)}
/>
每次父組件重新渲染時,您的組件Section
都會重新渲染,即使其他 props 沒有更改並且您使用React.memo
。 因為每次父組件渲染時,您的回調都會重新創建。 您應該將回調包裝在useCallback
鈎子中。
initialForm
這樣的復雜對象,使用useState
不是一個好的決定。 最好使用useReducer
;在這里你可以看到工作解決方案: https : //codesandbox.io/s/o10p05m2vz
我建議使用生命周期方法來防止重新渲染,在您可以使用的 react hooks 示例中,useEffect。 在上下文中集中你的狀態並使用 useContext 鈎子也可能會有所幫助。
通過一個復雜的表單來解決這個問題,我實施的技巧是在組件的輸入元素上使用onBlur={updateFormState}
來觸發將表單數據從組件提升到父表單,通過一個作為道具傳遞給組件的函數。
為了更新組件的輸入元素,我使用了組件內的狀態onChange={handleInput}
,然后當輸入(或組件作為一個整體,如果組件中有多個輸入字段)時,該組件狀態通過提升函數傳遞) 失去焦點。
這有點hacky,並且由於某種原因可能是錯誤的,但它在我的機器上工作。 ;-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.