自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

10個案例讓你徹底理解React hooks的渲染邏輯

開發(fā) 前端
由于項目環(huán)境比較復(fù)雜,如果是純class組件,那么就是component、pureComponent、shouldComponentUpdate之類的控制一下是否重新渲染,但是hooks似乎更多場景,接下來一一攻破。

正式開始,今天要寫什么呢,原本我對react原理非常清楚,自己寫過簡單的react,帶diff算法和異步更新隊列的,但是對hooks源碼一知半解,于是就要深究他的性能相關(guān)問題了   - 重復(fù)渲染的邏輯

由于項目環(huán)境比較復(fù)雜,如果是純class組件,那么就是component、pureComponent、shouldComponentUpdate之類的控制一下是否重新渲染,但是hooks似乎更多場景,接下來一一攻破。

  •  場景一 ,父組件使用hooks,子組件使用class Component

 父組件 

  1. export default function Test() {  
  2.     const [state, setState] = useState({ a: 1, b: 1, c: 1 });  
  3.     const [value, setValue] = useState(11);  
  4.     return (  
  5.         <div>  
  6.             <div>  
  7.                 state{state.a},{state.b}  
  8.             </div>  
  9.             <Button  
  10.                 type="default"  
  11.                 onClick={() => {  
  12.                     //@ts-ignore  
  13.                     setState({ a: 2, b: 1 });  
  14.                     //@ts-ignore  
  15.                     setState({ a: 2, b: 2 });  
  16.                     console.log(state, 'state');  
  17.                 }}  
  18.             >  
  19.                 測試  
  20.             </Button>  
  21.             <hr />  
  22.             <div>value{value}</div>  
  23.             <Button  
  24.                 type="default"  
  25.                 onClick={() => {  
  26.                     setValue(value + 1);  
  27.                 }}  
  28.             >  
  29.                 測試  
  30.             </Button>  
  31.             <Demo value={state} />  
  32.         </div>  
  33.     );  

子組件 

  1. export default class App extends React.Component<Props> {  
  2.     render() {  
  3.         const { props } = this;  
  4.         console.log('demo render');  
  5.         return (  
  6.             <div>  
  7.                 {props.value.a},{props.value.b}  
  8.             </div>  
  9.         );  
  10.     }  

結(jié)果每次點擊圖中的測試按鈕,子組件Demo都會重新render: 

總結(jié):父組件(hook)每次更新,都會導(dǎo)出一個新的state和value對象,子組件肯定會更新(如果不做特殊處理)

  •  場景二,父組件使用hooks,子組件使用class PureComponent 

父組件代碼跟上面一樣,子組件使用PureComponent: 

  1. export default function Test() {  
  2.     const [state, setState] = useState({ a: 1, b: 1, c: 1 });  
  3.     const [value, setValue] = useState(11);  
  4.     return (  
  5.         <div>  
  6.             <div>  
  7.                 state{state.a},{state.b}  
  8.             </div>  
  9.             <Button  
  10.                 type="default"  
  11.                 onClick={() => {  
  12.                     //@ts-ignore  
  13.                     setState({ a: 2, b: 1 });  
  14.                     //@ts-ignore  
  15.                     setState({ a: 2, b: 2 });  
  16.                     console.log(state, 'state');  
  17.                 }}  
  18.             >  
  19.                 測試  
  20.             </Button>  
  21.             <hr />  
  22.             <div>value{value}</div>  
  23.             <Button  
  24.                 type="default"  
  25.                 onClick={() => {  
  26.                     setValue(value + 1);  
  27.                 }}  
  28.             >  
  29.                 測試  
  30.             </Button>  
  31.             <Demo value={state} />  
  32.         </div>  
  33.     );  

子組件使用PureComponent: 

  1. export default class App extends React.PureComponent<Props> {  
  2.     render() {  
  3.         const { props } = this;  
  4.         console.log('demo render');  
  5.         return (  
  6.             <div>  
  7.                 {props.value.a},{props.value.b}  
  8.             </div>  
  9.         );  
  10.     }  

結(jié)果子組件依舊會每次都重新render:

總結(jié):結(jié)論同上,確實是依賴的props改變了,因為父組件是hook模式,每次更新都是直接導(dǎo)出新的value和state.

  •  場景三,搞懂hook的setState跟class組件setState有什么不一樣

理論:class的setState,如果你傳入的是對象,那么就會被異步合并,如果傳入的是函數(shù),那么就會立馬執(zhí)行替換,而hook的setState是直接替換,那么setState在hook中是異步還是同步呢?

實踐:

組件A: 

  1. export default function Test() {  
  2.     const [state, setState] = useState({ a: 1, b: 1, c: 1 });  
  3.     const [value, setValue] = useState(11);  
  4.     return (  
  5.         <div>  
  6.             <div>  
  7.                 state{state.a},{state.b},{state.c}  
  8.             </div>  
  9.             <Button  
  10.                 type="default"  
  11.                 onClick={() => {  
  12.                     //@ts-ignore  
  13.                     setState({ a: 2 });  
  14.                     //@ts-ignore  
  15.                     setState({ b: 2 });  
  16.                     console.log(state, 'state');  
  17.                 }}  
  18.             >  
  19.                 測試  
  20.             </Button>  
  21.             <hr />  
  22.             <div>value{value}</div>  
  23.             <Button  
  24.                 type="default"  
  25.                 onClick={() => {  
  26.                     setValue(value + 1);  
  27.                 }}  
  28.             >  
  29.                 測試  
  30.             </Button>  
  31.             <Demo value={state} />  
  32.         </div>  
  33.     );  

我將setState里兩次分別設(shè)置了state的值為{a:2},{b:2},那么是合并,那么我最終得到state應(yīng)該是{a:2,b:2,c:1},如果是替換,那么最后得到的state是{b:2}

結(jié)果:

點擊測試按鈕后,state變成了{(lán)b:2},整個value被替換成了{(lán)b:2}

結(jié)論:hook的setState是直接替換,而不是合并

  •  場景四 , 父組件使用class,子組件使用hook

    父組件: 

  1. export default class App extends React.PureComponent {  
  2.     state = {  
  3.         count: 1,  
  4.     };  
  5.     onClick = () => {  
  6.         const { count } = this.state;  
  7.         this.setState({  
  8.             count: count + 1,  
  9.         });  
  10.     };  
  11.     render() {  
  12.         const { count } = this.state;  
  13.         console.log('father render');  
  14.         return (  
  15.             <div>  
  16.                 <Demo count={count} />  
  17.                 <Button onClick={this.onClick}>測試</Button>  
  18.             </div>  
  19.         );  
  20.     }  

子組件: 

  1. interface Props {  
  2.     count: number;  
  3.  
  4. export default function App(props: Props) {  
  5.     console.log(props, 'props');  
  6.     return <div>{props.count}</div> 

邏輯:父組件(class組件)調(diào)用setState,刷新自身,然后傳遞給hooks子組件,然后自組件重新調(diào)用,更新

  •  場景五

但是我此時需要想實現(xiàn)一個class 組件的 PureComponent一樣的效果,需要用到React.memo

修改父組件代碼為: 

  1. export default class App extends React.PureComponent {  
  2.     state = {  
  3.         count: 1,  
  4.         value: 1,  
  5.     };  
  6.     onClick = () => {  
  7.         const { value } = this.state;  
  8.         this.setState({  
  9.             count: value + 1,  
  10.         });  
  11.     };  
  12.     render() {  
  13.         const { count, value } = this.state;  
  14.         console.log('father render');  
  15.         return (  
  16.             <div>  
  17.                 <Demo count={count} />  
  18.                 {value}  
  19.                 <Button onClick={this.onClick}>測試</Button>  
  20.             </div>  
  21.         );  
  22.     }  

子組件加入memo,代碼修改為: 

  1. import React, { useState, memo } from 'react';  
  2. interface Props {  
  3.     count: number;  
  4.  
  5. function App(props: Props) {  
  6.     console.log(props, 'props');  
  7.     return <div>{props.count}</div> 
  8.  
  9. export default memo(App); 

此時邏輯:class組件改變了自身的state,自己刷新自己,由上而下,傳遞了一個沒有變化的props給hooks組件,hooks組件使用了memo包裹自己。

結(jié)果:

我們使用了memo實現(xiàn)了PureComponent的效果,淺比較了一次

  •  場景六,hook,setState每次都是相同的值 
  1. export default class App extends React.PureComponent {  
  2.     state = {  
  3.         count: 1,  
  4.         value: 1,  
  5.     };  
  6.     onClick = () => {  
  7.         const { value } = this.state;  
  8.         this.setState({  
  9.             value:   1,  
  10.         });  
  11.     };  
  12.     render() {  
  13.         const { count, value } = this.state;  
  14.         console.log('father render');  
  15.         return (  
  16.             <div>  
  17.                 <Demo count={count} />  
  18.                 {value}  
  19.                 <Button onClick={this.onClick}>測試</Button>  
  20.             </div>  
  21.         );  
  22.     }  

結(jié)果:由于每次設(shè)置的值都是一樣的(都是1),hooks不會更新,同class

  •  場景七,父組件和子組件都使用hook

父組件傳入count給子組件 

  1. export default function Father() {  
  2.     const [count, setCount] = useState(1);  
  3.     const [value, setValue] = useState(1);  
  4.     console.log('father render')  
  5.     return (  
  6.         <div>  
  7.             <Demo count={count} />  
  8.             <div>value{value}</div>  
  9.             <Button  
  10.                 onClick={() => {  
  11.                     setValue(value + 1);  
  12.                 }}  
  13.             >  
  14.                 測試  
  15.             </Button>  
  16.         </div>  
  17.     );  

子組件使用count 

  1. export default function App(props: Props) {  
  2.     console.log(props, 'props');  
  3.     return <div>{props.count}</div> 

結(jié)果:每次點擊測試,都會導(dǎo)致子組件重新render

子組件加入memo 

  1. function App(props: Props) {  
  2.     console.log(props, 'props');  
  3.     return <div>{props.count}</div> 
  4.  
  5. export default memo(App); 

結(jié)果:

子組件并沒有觸發(fā)更新

這里跟第一個案例class的PureComponent不一樣,第一個案例class的PureComponent子組件此時會重新render,是因為父組件hooks確實每次更新都會導(dǎo)出新的value和state。這里是調(diào)用了一次,設(shè)置的都是相同的state.所以此時不更新

  •  場景八,父組件hook,子組件hook,使用useCallback緩存函數(shù)

父組件: 

  1. export default function App() {  
  2.   const [count1, setCount1] = useState(0);  
  3.   const [count2, setCount2] = useState(0); 
  4.   const handleClickButton1 = () => {  
  5.     setCount1(count1 + 1);  
  6.   };  
  7.   const handleClickButton2 = useCallback(() => {  
  8.     setCount2(count2 + 1);  
  9.   }, [count2]);   
  10.   return (  
  11.     <div>  
  12.       <div>  
  13.         <Button onClickButton={handleClickButton1}>Button1</Button>  
  14.       </div>  
  15.       <div>  
  16.         <Button onClickButton={handleClickButton2}>Button2</Button>  
  17.       </div>  
  18.     </div>  
  19.   );  

子組件: 

  1. import React from 'react';  
  2. const Button = (props: any) => { 
  3.     const { onClickButton, children } = props;  
  4.     return (  
  5.         <>  
  6.             <button onClick={onClickButton}>{children}</button>  
  7.             <span>{Math.random()}</span>  
  8.         </>  
  9.     );  
  10. };  
  11. export default React.memo(Button); 

結(jié)果:雖然我們使用了memo.但是點擊demo1,只有demo1后面的數(shù)字改變了,demo2沒有改變,點擊demo2,兩個數(shù)字都改變了。

那么我們不使用useCallback看看

父組件修改代碼,去掉useCallback 

  1. export default function App() {  
  2.     const [count1, setCount1] = useState(0);  
  3.     const [count2, setCount2] = useState(0);  
  4.     const handleClickButton1 = () => {  
  5.         setCount1(count1 + 1);  
  6.     };  
  7.     const handleClickButton2 = () => {  
  8.         setCount2(count2+ 1);  
  9.     };  
  10.     return (  
  11.         <div>  
  12.             <div>  
  13.                 <Demo onClickButton={handleClickButton1}>Demo1</Demo>  
  14.             </div>  
  15.             <div>  
  16.                 <Demo onClickButton={handleClickButton2}>Demo</Demo>  
  17.             </div>  
  18.         </div>  
  19.     );  

子組件代碼不變,結(jié)果此時每次都會兩個數(shù)字都會跟著變。

官方對useCallback的解釋:

就是返回一個函數(shù),只有在依賴項發(fā)生變化的時候才會更新(返回一個新的函數(shù))

結(jié)論:

我們聲明的 handleClickButton1 是直接定義了一個方法,這也就導(dǎo)致只要是父組件重新渲染(狀態(tài)或者props更新)就會導(dǎo)致這里聲明出一個新的方法,新的方法和舊的方法盡管長的一樣,但是依舊是兩個不同的對象,React.memo 對比后發(fā)現(xiàn)對象 props 改變,就重新渲染了。 

  1. const a =()=>{}  
  2. const b =()=>{}  
  3. a===b //false 

這個道理大家都懂,不解釋了

  •  場景九,去掉依賴數(shù)組中的count2字段 
  1. import React, { useState, useCallback } from 'react';  
  2. import Demo from './Demo'; 
  3.  export default function App() {  
  4.   const [count2, setCount2] = useState(0);  
  5.   const handleClickButton2 = useCallback(() => {  
  6.     setCount2(count2 + 1);  
  7.   }, []);  
  8.   return (  
  9.     <Demo   
  10.       count={count2}  
  11.       onClickButton={handleClickButton2}  
  12.     >測試</Demo>  
  13.   );  

這樣count2的值永遠(yuǎn)都是0,那么這個組件就不會重導(dǎo)出setCount2這個方法,handleClickButton2這個函數(shù)永遠(yuǎn)不會變化,Button只會更新一次,就是Demo組件接受到的props從0到1到的時候.繼續(xù)點擊,count2也是0,但是props有一次從0-1的過程導(dǎo)致Demo子組件被更新,不過count2始終是0,這非常關(guān)鍵

  •  場景十,使用useMemo,緩存對象,達(dá)到useCallback的效果

使用前 

  1. export default function App() {  
  2.     const [count, setCount] = useState(0);  
  3.     const [value, setValue] = useState(0);  
  4.     const userInfo = {  
  5.         age: count,  
  6.         name: 'Jace',  
  7.     };  
  8.     return (  
  9.         <div>  
  10.             <div>  
  11.                 <Demo userInfo={userInfo} />  
  12.             </div>  
  13.             <div>  
  14.                 {value}  
  15.                 <Button  
  16.                     onClick={() => {  
  17.                         setValue(value + 1);  
  18.                     }}  
  19.                 ></Button>  
  20.             </div>  
  21.         </div>  
  22.     );  

子組件使用了memo,沒有依賴value,只是依賴了count.

但是結(jié)果每次父組件修改了value的值后,雖然子組件沒有依賴value,而且使用了memo包裹,還是每次都重新渲染了

 

  1. import React from 'react';  
  2. const Button = (props: any) => {  
  3.     const { userInfo } = props;  
  4.     console.log('sub render');  
  5.     return (  
  6.         <>  
  7.             <span>{userInfo.count}</span>  
  8.         </>  
  9.     );  
  10. };  
  11. export default React.memo(Button); 

使用后useMemo 

  1. const [count, setCount] = useState(0);  
  2. const obj = useMemo(() => {  
  3.   return {  
  4.     name: "Peter",  
  5.     age: count  
  6.   };  
  7. }, [count]);  
  8. return <Demo obj={obj}> 

很明顯,第一種方式,如果每次hook組件更新,那么hook就會導(dǎo)出一個新的count,const 就會聲明一個新的obj對象,即使用了memo包裹,也會被認(rèn)為是一個新的對象。

看看第二種的結(jié)果:

父組件更新,沒有再影響到子組件了。

寫在最后:

為什么花了將近4000字來講React hooks的渲染邏輯,React的核心思想,就是拆分到極致的組件化。拆得越細(xì)致,性能越好,避免不必要的更新,就是性能優(yōu)化的基礎(chǔ),希望此文能真正幫助到你了解hook的渲染邏輯 

 

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2020-11-03 10:32:48

回調(diào)函數(shù)模塊

2021-02-05 10:57:03

邊緣計算首席運營官

2022-06-23 09:04:14

ReactHooks項目

2020-03-12 14:40:59

Python表格命令行

2021-05-11 08:48:23

React Hooks前端

2022-10-29 08:55:19

頁面react

2022-08-21 09:41:42

ReactVue3前端

2021-02-07 21:59:39

Java回調(diào)機制

2022-05-04 10:38:58

React閉包組件

2022-05-05 08:31:48

useRefuseEffecthook

2018-12-18 10:43:07

2020-08-10 06:31:01

React Hooks前端開發(fā)

2018-06-19 14:52:52

2010-08-27 11:00:05

秘訣

2021-03-30 18:11:05

Python工具代碼

2019-08-20 15:16:26

Reacthooks前端

2024-03-15 08:23:26

異步編程函數(shù)

2021-04-07 13:28:21

函數(shù)程序員異步

2012-05-09 09:49:57

移動支付

2023-11-06 08:00:00

ReactJavaScript開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號