美文网首页
React memo

React memo

作者: l1n3x | 来源:发表于2022-04-17 09:55 被阅读0次

在 react 中如果数据没发生变化,则真实的 dom 不会发生改变。但是 dom 不发生改变并不代表 react 中不会产生其他耗时的计算。如果一个组件会产生大量的子组件,那单单是这些子组件 diff 就会产生大量的耗时操作,在某些场景下可能会引起页面卡顿。

一个例子

这里有一个典型的例子,父组件 App 中有一个输入框和子组件 Child。Child 接受来自父组件的状态 text。从代码中可以看出,这个 text 一直保持不变,丝毫不受 input 的影响。那么如果这时候输入的值发生了改变,子组件是否会 render 呢(这里指子组件是否会被调用并且 diff,并不是指真实的 dom render)?

function Child(props){
  console.info("child call render")
  return (
    <p>{this.props.text}</p>
  )
}
function App() {
  const [value, setValue] = useState()
  const [text, setText] = useState({text: 'hello'})
  return  (
    <>
      <input onChange = {e => setValue(e.target.value)} />
      <Child text={hello}/>
    </>
  )
}

答案是: 会!。也就是虽然父组件传递给子组件的值没有发生任何改变,但实际上子组件仍然会 render。其原因也非常简单:

  1. react 如果判断本次 props 和上一次 props 不一致,则一定会 render 该组件,但是这个不一致指的是 === 而不是 props 的内容。
  2. <Child text={hello}/> 实际上是转换成了 React.createElement(Child, {text: 'hello'}))。而这里的 {text: 'hello'} 则是传递给 Child 的 props。每当 App 状态发生改变时,都会创建一个新的匿名对象 {text: 'hello'}。虽然内容不同,但是引用却改变了。因此导致了 Child render。

React.memo

这样的情况,类组件可以通过 componentShouldUpdate 来实现用户自定义的比较。而对于函数组件在没有对应的生命周期的情况下,则可以采用 React.memo 来解决这个问题。React.memo 接受两个参数:

  1. 需要 memo 的组件
  2. compare 方法(默认为 shallowEqual)

即利用该方法可以实现与 componentShouldUpdate 相似的功能,每次比较时不采用 === 而是采用 compare 方法,当该方法返回 true 则表示 props 没有变化。shallowEqual 也非常简单:

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);
  if (keysA.length !== keysB.length) {
    return false;
  }
  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }
  return true;
}

而 React.memo 的实现更是非常简洁:

function memo(type, compare) {
  {
    if (!isValidElementType(type)) {
      error('memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type);
    }
  }

  return {
    $$typeof: REACT_MEMO_TYPE,
    type: type,
    compare: compare === undefined ? null : compare
  };
}

几乎没有做任何事情,只是将该组件声明为 REACT_MEMO_TYPE 告诉 React 比较时不要采用 === 而是采用用户自定义的比较函数或者默认采用 shallowEqual

相关文章

网友评论

      本文标题:React memo

      本文链接:https://www.haomeiwen.com/subject/yqshertx.html