# React 優化項目(一):React.memo ###### tags: `React` `Optimize` `Hooks` `memo` `OptimizeRender` React 優化項目系列,減少大量且不必要的元件重新渲染週期 #### [React 優化項目(一):React.memo](https://hackmd.io/@yellow/optimize-render) > 如何觸發更新的 <br />紀錄 props value #### [React 優化項目(二):useCallback](https://hackmd.io/@yellow/optimize-render-usecallback) > 紀錄 func 記憶體位置 #### [React 優化項目(三):useMemo](https://hackmd.io/@yellow/optimizer-render-useMemo) > 紀錄 object value 避免重複執行相關處理 --- ## 如何觸發元件更新? 再開始之前先來了解什麼樣的情況會使得元件更新 - `props` : 父層傳進來的 props 變動 - `state` : 元件本身 state 變動 - `context` : 上層 context 變動 <br /> ## 元件的更新(render)分為 - `re-evaluating` 重新執行(評估) - `re-render` 重新渲染 觸發到元件更新時,React 會先做出 `re-evaluating` 用這次更新的虛擬 DOM 比對上次真實 DOM 的 `snapshot`,這僅僅是 React 重新執行,不代表真實 DOM 的各個部分被重新渲染,而當比對 `snapshot`發現需要更新時才去做 `re-render` 對 `Real DOM` 做更新。 <br /> ## 元件 Render 範例測試 先來看看 components render 情況,下例是三層元件 - 父元件 `RenderTry` 引用 `DemoOutput` 引用 `Paragraph` - 每個元件內都有自己的 log。 #### 父元件 `RenderTry` - 當按下按鈕時會更新 `showParagraph` 並傳入 `DemoOutput` ``` export default function RenderTry() { const [showParagraph, setShowParagraph] = useState(false); console.log('APP RUNNING'); const toggleParagraph = () => { setShowParagraph((pre) => !pre); }; return ( <div> <h1>Hi There!</h1> {/* 引用 DemoOutput 並傳入 props */} <DemoOutput show={showParagraph} /> <button onClick={toggleParagraph}>Toggle paragraph</button> </div> ); } ``` #### 子元件 `DemoOutput` - 接收 `props.show` 並傳入 `children` 至 `Paragraph` ``` const DemoOutput = (props) => { console.log('DemoOutput RUNNING!'); {/* 引用 Paragraph 並傳入 props */} return <Paragraph>{props.show ? 'This is new!' : ''}</Paragraph>; }; export default DemoOutput; ``` #### 子元件 `Paragraph` - 接收 `props.childrend` 並顯示 ``` const Paragraph = (props) => { console.log('Paragraph RUNNING!'); return <p>{props.children}</p>; } export default Paragraph; ``` #### 執行更新觀察 可以看到跑了一組 `APP RUNNING` (包含裡面元件的 log) 點擊按鈕時又會再跑一組 `APP RUNNING`,之後的每次點擊都會再跑一組  因為有更新顯示所以 Real DOM 也會更新  這樣是沒問題的 因為 `RenderTry` 更新了 `state` 處發了更新並連同底下的元件也被觸發了更新。 ### 那現在來嘗試 不更新第二層 `DemoOutput` 這個元件 把傳進 `DemoOutput` 的 `props` 寫死,測試不讓 `DemoOutput` 元件重新渲染 ``` {/* 引用 DemoOutput 並傳入 props */} <DemoOutput show={false} /> ``` 發現`APP RUNNING` 還是一樣跑了一組,但 Real DOM 沒有變化。 #### 為什麼會這樣呢? 因為父元件 `RenderTry` 更新了 `state` 代表他整個元件都更新了,所以會連同在內的元件都做了 `re-evaluation` 的更新,但因為經 `re-evaluation` 評估後 Real DOM 是不需要更新的,所以沒有執行 `re-render`。 為了要減少 React 不必要的 `re-evaluation` 可以使用 `React.memo` <br /> ## 使用 `React.memo()` > **使用`React.memo` 儲存之前的 props value** ``` const DemoOutput = (props) => { console.log('DemoOutput RUNNING!'); return <Paragraph>{props.show ? 'This is new!' : ''}</Paragraph>; }; // 加上 memo export default React.memo(DemoOutput); ```  重新整理跑出一組 `APP RUNING`,但之後點擊按鈕更新 state 都只有父層做更新而已。 那我是不是趕緊把所有元件都加上 memo 啦! ### React.memo 是有代價的!! 他會去做兩件事: - 需要去儲存 之前的 props value - 需要去比較 新舊兩個的差別 > 所以建議是在底下有多層時,加在較上層的元件入口,例如範例一樣,不是加再最底層的 `Paragraph` 元件上,而是加在元件樹的根源處 `DemoOutput` 上。 繼續往下看 React.useCallback
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up