注意:hooks只能在函數(無狀態組件)中使用html
useMome、useCallback用法都差很少,都會在第一次渲染的時候執行,以後會在其依賴的變量發生改變時再次執行,而且這兩個hooks都返回緩存的值,useMemo返回緩存的變量,useCallback返回緩存的函數。react
const value = useMemo(fnM, [a]);
const fnA = useCallback(fnB, [a]);
複製代碼
React.memo 爲高階組件。它與React.PureComponent很是類似,但它適用於函數組件,但不適用於 class 組件。api
默認狀況下其只會對複雜對象作淺層對比,若是你想要控制對比過程,那麼請將自定義的比較函數經過第二個參數傳入來實現。這與shouldComponentUpdate 方法的返回值相反。數組
function MyComponent(props) { /* 使用 props 渲染 */ } function areEqual(prevProps, nextProps) { /* 若是把 nextProps 傳入 render 方法的返回結果與 將 prevProps 傳入 render 方法的返回結果一致則返回 true, 不然返回 false */ } export default React.memo(MyComponent, areEqual); 複製代碼
當父組件引入子組件的狀況下,每每會形成組件之間的一些沒必要要的浪費,下面咱們經過例子來了解一下場景緩存
const Child = (props) => { console.log('子組件?') return( <div>我是一個子組件</div> ); } const Page = (props) => { const [count, setCount] = useState(0); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <Child /> </> ) } 複製代碼
const Child = (props) => { console.log('子組件?') return( <div>我是一個子組件</div> ); } const ChildMemo = memo(Child); const Page = (props) => { const [count, setCount] = useState(0); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <ChildMemo /> </> ) } 複製代碼
當父組件傳遞狀態給子組件的時候,memo好像沒什麼效果,子組件仍是執行了,這時候咱們就要引入hooks的useCallback、useMemo這兩個鉤子了。性能優化
//子組件會有沒必要要渲染的例子 interface ChildProps { name: string; onClick: Function; } const Child = ({ name, onClick}: ChildProps): JSX.Element => { console.log('子組件?') return( <> <div>我是一個子組件,父級傳過來的數據:{name}</div> <button onClick={onClick.bind(null, '新的子組件name')}>改變name</button> </> ); } const ChildMemo = memo(Child); const Page = (props) => { const [count, setCount] = useState(0); const [name, setName] = useState('Child組件'); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <ChildMemo name={name} onClick={(newName: string) => setName(newName)}/> </> ) } 複製代碼
在上面代碼基礎上,父級調用子級時,在onClick參數上加上useCallback,參數爲[],則第一次初始化結束後,不在改變。bash
//子組件沒有必要渲染的例子 interface ChildProps { name: string; onClick: Function; } const Child = ({ name, onClick}: ChildProps): JSX.Element => { console.log('子組件?') return( <> <div>我是一個子組件,父級傳過來的數據:{name}</div> <button onClick={onClick.bind(null, '新的子組件name')}>改變name</button> </> ); } const ChildMemo = memo(Child); const Page = (props) => { const [count, setCount] = useState(0); const [name, setName] = useState('Child組件'); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/> {/* useCallback((newName: string) => setName(newName),[]) */} {/* 這裏使用了useCallback優化了傳遞給子組件的函數,只初始化一次這個函數,下次不產生新的函數 </> ) } 複製代碼
//子組件會有沒必要要渲染的例子 interface ChildProps { name: { name: string; color: string }; onClick: Function; } const Child = ({ name, onClick}: ChildProps): JSX.Element => { console.log('子組件?') return( <> <div style={{ color: name.color }}>我是一個子組件,父級傳過來的數據:{name.name}</div> <button onClick={onClick.bind(null, '新的子組件name')}>改變name</button> </> ); } const ChildMemo = memo(Child); const Page = (props) => { const [count, setCount] = useState(0); const [name, setName] = useState('Child組件'); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <ChildMemo name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green'}} onClick={ useCallback((newName: string) => setName(newName), []) } /> </> ) } 複製代碼
更新屬性name爲對象類型,這時子組件仍是同樣的執行了,在父組件更新其它狀態的狀況下,子組件的name對象屬性會一直髮生從新渲染改變,從而致使一直執行,這也是沒必要要的性能浪費。markdown
解決這個問題,使用name參數使用useMemo,依賴於State.name數據的變化進行更新函數
interface ChildProps { name: { name: string; color: string }; onClick: Function; } const Child = ({ name, onClick}: ChildProps): JSX.Element => { console.log('子組件?') return( <> <div style={{ color: name.color }}>我是一個子組件,父級傳過來的數據:{name.name}</div> <button onClick={onClick.bind(null, '新的子組件name')}>改變name</button> </> ); } const ChildMemo = memo(Child); const Page = (props) => { const [count, setCount] = useState(0); const [name, setName] = useState('Child組件'); return ( <> <button onClick={(e) => { setCount(count+1) }}>加1</button> <p>count:{count}</p> <ChildMemo //使用useMemo,返回一個和本來同樣的對象,第二個參數是依賴性,當name發生改變的時候,才產生一個新的對象 name={ useMemo(()=>({ name, color: name.indexOf('name') !== -1 ? 'red' : 'green' }), [name]) } onClick={ useCallback((newName: string) => setName(newName), []) } {/* useCallback((newName: string) => setName(newName),[]) */} {/* 這裏使用了useCallback優化了傳遞給子組件的函數,只初始化一次這個函數,下次不產生新的函數 /> </> ) } 複製代碼
在子組件不須要父組件的值和函數的狀況下,只須要使用memo函數包裹子組件便可。而在使用函數的狀況,須要考慮有沒有函數傳遞給子組件使用useCallback。而在值有所依賴的項,而且是對象和數組等值的時候而使用useMemo(當返回的是原始數據類型如字符串、數字、布爾值,就不要使用useMemo了)。不要盲目使用這些hooks。oop
React進階用法和hooks的我的使用看法(Typescript版本) - 3.useCallback+useMemo+memo性能優化