什么是 React.memo ?
React.memo
和 React.PureComponent
类似, React.PureComponent
在类组件中使用,而React.memo
在函数组件中使用
看下面两个例子,有两个计数器组件,两个计数器的数据都来源于父组件,第一个计数器通过点击按钮数字会不断累加而发生改变,第二个计数器没有按钮控制数字改变。
const App = () => { const [count1, setCount1] = React.useState(0) const [count2, setCount2] = React.useState(0) const increaseCounter1 = () => { setCount1(count1 => count1 + 1) } return ( <> <button onClick={increaseCounter1}>Increase counter 1</button> <Counter value={count1}>Counter 1</Counter> <Counter value={count2}>Coutner 2</Counter> </> ) }
计数器组件
const Counter = ({value, children}) => { console.log('Render: ', children) return ( <div> {children}: {value} </div> ) } export default Counter
但是不管这个两个计算器的数据是否发生改变,Counter
组件都会输出 Render
来。现在我们尝试使用 React.memo
包裹这个组件。
const Counter = ({value, children}) => { console.log('Render: ', children) return ( <div> {children}: {value} </div> ) } export default React.memo(Counter)
React.memo
浅层对比 prop 和 state 的方式来实现了该函数。现在开始第二个计数器将不会重新渲染了,由于 prop 没有发生改变。
在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。
如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent
,或是手动实现 shouldComponentUpdate
方法。
useMemo and useCallback
useMemo 相当于Vue中computed里的计算属性,当某个依赖项改变时才重新计算值,这种优化有助于避免在每次渲染时都进行高开销的计算。
React.useMemo(() => { fooFunction() }, [dependencies])
React.useCallback(() => { fooFunction() }, [dependencies])
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
useCallback
和 useMemo
参数相同,第一个参数是函数,第二个参数是依赖项的数组。主要区别是 React.useMemo
将调用 fn
函数并返回其结果,而 React.useCallback
将返回 fn
函数而不调用它。
看看下来例子
const App = () => { const fooFunction = () => { return 'Foo is just Food without D' } const useMemoResult = React.useMemo(fooFunction, []) const useCallbackResult = React.useCallback(fooFunction, []) console.log('useMemoResult: ', useMemoResult) console.log('useCallbackResult: ', useCallbackResult) return <p>Foo is just food without D</p> }
最后得到的输出结果
const Me = ({girlFriendWords}) => { // Provided that girlFriendWords is a string const myReply = decideWhatToSay (girlFriendWords) return <p>{myReply}</p> }
代码中计算 myReply
值,默认每次组件渲染的时候都会重新执行
const Me = ({girlFriendWords}) => { // Provided that girlFriendWords is a string const myReply = React.useMemo(() => decideWhatToSay (girlFriendWords), [girlFriendWords]) return <p>{myReply}</p> }
使用 React.useMemo
通过 [girlFriendWords]
作为依赖项,当依赖的值发生改变,函数才会重新执行 decideWhatToSay
useCallback
使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;
import React, { useMemo, useCallback } from "react" let Counter = ({ value, children, onClick }) => { console.log('Render: ', children) return ( <div onClick={onClick}> {children}: {value} </div> ) } Counter = React.memo(Counter) const App = () => { const [count1, setCount1] = React.useState(0) const [count2, setCount2] = React.useState(0) const increaseCounter1 = () => { setCount1(count1 => count1 + 1) } const increaseCounter2 = () => { setCount2(count2 => count2 + 1) } return ( <> <Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter> <Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter> </> ) } export default App
但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
import React, { useMemo, useCallback } from "react" let Counter = ({ value, children, onClick }) => { console.log('Render: ', children) return ( <div onClick={onClick}> {children}: {value} </div> ) } Counter = React.memo(Counter) const App = () => { const [count1, setCount1] = React.useState(0) const [count2, setCount2] = React.useState(0) const increaseCounter1 = useCallback(() => { setCount1(count1 => count1 + 1) }, []) const increaseCounter2 = useCallback(() => { setCount2(count2 => count2 + 1) }, []) console.log(increaseCounter1) return ( <> <Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter> <Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter> </> ) } export default App