React-Hooks 初识 (五): React性能优化手段 Memo 防止子组件不必要的reRender

1,238 阅读3分钟

  大家好,我是张添财。在上一篇文章结尾我给小伙伴们留个一个问题:react如何避免不必要的rerender? 今天先给大家填一部分坑,介绍下React里memo这个API。闲话不说,卷就完事了。

  一句话总结memo的作用就是:在子组件state和props都没变的前提下,避免父组件render时造成子组件的渲染。下面我将列举三个demo帮助大家了解memo的用法。

1、子组件不包裹memo

  如下所示,当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变,子组件也发生了render。

import React, { memo, useState } from 'react';

// 子组件
const ChildComp = () => {
  console.log('ChildComp又进行渲染了');
  return (<div>ChildComp...</div>);
};

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <div>hello world {count}</div>
      <div onClick={() => { setCount(count => count + 1); }}>点击增加</div>
      <ChildComp/>
    </div>
  );
};

export default Parent;

image.png 解析: 运行这段代码后,我们可以看到:点击按钮,改变了父组件的count值,父组件重新渲染,此时子组件的props和state值都没有发生变化,但是子组件ChildComp也进行了重新渲染;Memo的引入就是为了防止这种情况的发生。

2、用memo进行包裹避免子组件渲染

  还是用上面的demo,不同的是我们使用 memo 把子组件ChildComp进行包裹

import React, { memo, useState } from 'react';

// 子组件用memo进行包裹
const ChildComp =memo(() => {
  console.log('ChildComp又进行渲染了');
  return (<div>ChildComp...</div>);
})

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <div>hello world {count}</div>
      <div onClick={() => { setCount(count => count + 1); }}>点击增加</div>
      <ChildComp/>
    </div>
  );
};

export default Parent;

image.png   由改进后的代码我们可以发现,利用memo包裹子组件,就能父组件render造成子组件也渲染的问题。但是注意:memo包裹子组件只能解决父组件没有传参给子组件的情况或者父组件传简单数据类型的参数给子组件的情况(例如 string、number、boolean等)

3、memo已经包裹,但父组件传给子组件的参数是复杂数据类型,那么子组件仍会渲染

  看完上面两个demo相信小伙伴们应该对memo的用法有了一定的了解。但这里我要补充一点,如果父组件给子组件传的是一个复杂数据类型的props结果又如何呢?大家看以运行下面的代码:

import React, { memo, useState } from 'react'
function Parent() {
    const fValue = { name: 'aaa' }
    const [count, setCount] = useState(0)
    return (
        <>
            内含子组件,这里传入的props是个复杂数据类型
            <ChildComp value={fValue} />
            <button onClick={() => setCount(count + 1)}>点击改变count值{count}</button>
        </>
    )
}
const ChildComp = memo((props) => {
    console.log('子组件又渲染了' + props.value.name);
    return (
        <>w(゚Д゚)w</>
    )
}
)
export default Parent

image.png

  这里给子组件传入的是一个对象类型( 父组件定义了props 并且传给了子组件的 value 属性 ,子组件接受的是一个复杂数据类型 ),但是我们运行后发现,每次点击button时父组件渲染了,子组件虽然用memo包裹,但子组件还是rerender了。

  解析:这是因为,由于子组件接收了父组件的fValue,并且类型是个对象,每次父组件render时这个fValue都是新的,从而造成子组件在每次父组件render时props都会发生变化,也就重新进行渲染了。

  那么问题来了:我们在业务开发中最常用到的就是对象,那react是怎么避免上述的情况呢?各位看官别急,下一篇我们继续说道说道useMemo解决父组件传给子组件的参数是复杂数据类型,子组件仍会渲染的问题。小伙伴们感兴趣的点个关注呐。