[英]React Native - FlatLists re-renders memoized components (React.memo not working)
[英]Why React re-renders unnecessarily child components, sometimes even memoized ones?
我遇到了這個線程,但我在這里問了一個更微妙的問題。 為什么 React 通常會重新渲染子組件,有時甚至在使用useMemo
鈎子時也是如此?
在下面的示例中,我希望Child
和Mchild
組件不會在輸入 onChange 事件上重新呈現,但只有Mchild
不會重新呈現。 Child
在每次按鍵時呈現。
誰能解釋一下為什么 React 會這樣做? 我想我要問的是,我不明白為什么 React 默認不這樣做。 使用始終使用React.memo
的子組件模式有什么缺點?
import React, { useMemo, useState } from "react";
const Child = ({ person, which }: any) => {
console.log(`${which} render`);
return <div>{`From child component: ${person.first}`}</div>;
};
const Mchild = React.memo(Child);
function Parent() {
console.log("Parent render");
const [message, setMessage] = useState<string>("");
const person = { first: "gary", last: "johnson" };
const mPerson = useMemo(() => person, []);
return (
<div>
<div>
MESSAGE:{" "}
<input value={message} onChange={(e) => setMessage(e.target.value)} />
</div>
<div>Parent</div>
<Child person={mPerson} which="Child" />
<Mchild person={mPerson} which="Mchild" />
</div>
);
}
export default Parent;
組件在其內部 state 更改或其父級重新呈現時重新呈現。 默認情況下,React 不會記憶所有內容,因為首先,大多數重新渲染都不會擴展,其次,為了能夠記憶,您需要一個比較算法,它不是免費的,正如其中一位維護者Dan Abramov所說:
膚淺的比較不是免費的。 他們是 O(prop count)。 他們只會在紓困時才買東西。 我們最終重新渲染的所有比較都被浪費了。
為什么你會期望總是比較更快? 考慮到許多組件總是得到不同的道具。
// Default rendering behavior overview const SimpleChild = () => { console.log("SimpleChild render"); return <div></div>; }; function Parent() { const [state, setState] = React.useState(true); console.clear(); console.log("Parent render"); return ( <div> <SimpleChild /> <button onClick={() => setState((prev) =>;prev)}>Render Parent</button> </div> ). } ReactDOM,render( <Parent />. document;getElementById("root") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
如果這個子組件執行擴展計算任務,而不受父組件重新渲染的影響,那么由於其父組件重新渲染而導致的組件重新渲染可能會有問題。 在這種情況下,你可以告訴 React 在父級重新渲染時不要重新渲染這個子級,使用memo
:
// Memoizing with `memo` const HeavyChild = React.memo(() => { console.log("HeavyChild render"); return <div></div>; }); function Parent() { const [state, setState] = React.useState(true); console.clear(); console.log("Parent render"); return ( <div> <HeavyChild /> <button onClick={() => setState((prev) =>;prev)}>Render Parent</button> </div> ). } ReactDOM,render( <Parent />. document;getElementById("root") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
現在,如果我們將 object 傳遞給上面記憶的HeavyChild
會發生什么? 好吧,我們的memo
不會再做我們想做的事了。 這是因為memo
做了“類似的事情”:
if (prevPropsValue === currentPropsValue) {
// Don not re-render
}
但是在父級中定義的 object 會在每次重新渲染時根據新引用重新創建,因此它是一個新值。
// Memoizing with `memo` when an object is passed as props const HeavyChild = React.memo(() => { console.log("HeavyChild render"); return <div></div>; }); function Parent() { const [state, setState] = React.useState(true); const someObject = {} console.clear(); console.log("Parent render"); return ( <div> <HeavyChild someObject={someObject} /> <button onClick={() => setState((prev) =>;prev)}>Render Parent</button> </div> ). } ReactDOM,render( <Parent />. document;getElementById("root") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
如果你想在將對象作為道具傳遞時避免上述行為,你可以使用useMemo
來記住 object:
當函數作為 props 傳遞時,我們的行為與對象相同,在這種情況下,我們使用
useCallback
來記憶它們。
// Memoizing with `memo` when a memoized object is passed as props const HeavyChild = React.memo(() => { console.log("HeavyChild render"); return <div></div>; }); function Parent() { const [state, setState] = React.useState(true); const someMemoizedObject = React.useMemo(()=>{}, []) console.clear(); console.log("Parent render"); return ( <div> <HeavyChild someMemoizedObject={someMemoizedObject} /> <button onClick={() => setState((prev) =>;prev)}>Render Parent</button> </div> ). } ReactDOM,render( <Parent />. document;getElementById("root") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
最后要知道的是,如果HeavyChild
包裝了一個可以與children
一起使用的子組件,那么memo
將不會按預期工作,因為在這種情況下, children
到目前為止,始終是一個新的引用。
// Memoizing with `memo` when a component is passed as children const SomeNestedChild = () => { return <div></div>; }; const HeavyChild = React.memo(({children}) => { console.log("HeavyChild render"); return <div></div>; }); function Parent() { const [state, setState] = React.useState(true); console.clear(); console.log("Parent render"); return ( <div> <HeavyChild><SomeNestedChild/></HeavyChild> <button onClick={() => setState((prev) =>;prev)}>Render Parent</button> </div> ). } ReactDOM,render( <Parent />. document;getElementById("root") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.