【青训营】- React · useMemo & useCallback

349 阅读1分钟

前言

默认情况下, 父组件的状态(state)发生变化,不仅会重新渲染自己,还会重新渲染其子组件。 因此,React 发明了 useMemo & useCallback 来帮助你减少不必要的渲染

此外还有, shouldComponentUpdate / React.PureComponent/ React.memo 在干类似的事情。

useMemo

const UseMemoPage = props => {
  const [counter, setCounter] = useState(0)
  const [value, setValue] = useState('')
  const total = () => {
    console.log('computed')
    let result = 0
    for (let i = 0; i < counter; i++) {
      result += i
    }
    return result
  }
  console.log(UseMemoPage.name, 'render')
  return (<>
    <p>{value}</p>
    
    /* 当你在更改 input 的值得时候, 也会 触发 total 不必要的运算, */
    
    <input value={value} onChange={(e) => setValue(e.target.value)} />
    {total()}
    <button onClick={() => setCounter(counter + 1)}>add</button>
  </>)
}
// console.log UseMemoPage render
// console.log computed
  • 通过useMemo改进, value 的改变不会触发的重新计算此外useMemo还可以返回一个React组件,有相同的效果
  const total = useMemo(() => {
    console.log('computed')
    let result = 0
    for (let i = 0; i < counter; i++) {
      result += i
    }
    return result
  }, [counter])

// console.log UseMemoPage render

useCallback

const UseCallbackPage = props => {
  const [counter, setCounter] = useState(0)
  const [value, setValue] = useState('')
  const addClick = () => {
    console.log(addClick.name, 'render')
    setCounter(state => state + 1)
  }
  console.log(UseCallbackPage.name, 'render')
  return (<>
    <p>{value}</p>
    <input value={value} onChange={(e) => setValue(e.target.value)} />
    <button onClick={() => setCounter(counter + 1)}>add</button>
    <Child counter={counter} addClick={addClick}></Child>
  </>)
}

const Child = React.memo((props) => {
  console.log('Child', 'render')
  return (<div onClick={props.addClick}>{props.counter}</div>)
})

// console.log UseMemoPage render
// console.log Child render
  • 虽然你使用React.memo , 但是value更新时, 都会生成新的addClick(只是名字一样), 所以会造成Child组件的渲染, 此时就是useCallback 登场的时机, 它能缓存你定义的function
  const addClick = useCallback(() => {
    console.log(addClick.name, 'render')
    setCounter(state => state + 1)
  }, [])
  
  // console.log UseCallbackPage render

总结:

  1. useCallback, useMemo 看起来就像是React的补丁, 但能很好的 减少不必要的渲染
  2. useMemo 返回的是个object, 一般适用于组件本身
  3. useCallback 返回的是一个function, 可以配合React.memo来减少Child组件的渲染
  4. 所以子组件尽量为无状态组件, 方便 开发 / 测试 / 优化