写了就忘实战系列——React中如何避免子组件re-rend

371 阅读3分钟

一。背景

React的性能优化的一个方面就是避免组件的重复渲染(re-rend)。官方文档也提供了很过性能优化的奇技淫巧,且大部分都有一个比较明确的上下文。

这里我们讨论的是情况:

  • 父组件修改state后,子组件无依赖传参,如何避免子组件re-rend,
  • 父组件修改state后,子组件有依赖传参,如何避免子组件re-rend。

二。实例

1. 如何避免无依赖传参时,子组件的渲染

function Child () {
    console.log('child re-rend')
    return (<div>我是儿砸。</div>)
}
function Parent() {
    const [count, setCount] = useState(0);
    
    return (<>
        {count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
        <Child />
    </>)
}

当我们点击父组件的按钮,修改state的值 5次 后,由于Parent的re-rend,导致了整个子组件re-rend了 5次。下图可以看到,初始输出 1次,重新渲染输出了 5次

1.png

避免方式————通过props.children

function Child () {
    console.log('child re-rend')
    return (<div>我是儿砸。</div>)
}
function Parent({children}) {
    const [count, setCount] = useState(0);
    
    return (<>
        {count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
        {children}
    </>)
}
// 顶级App组件
function App() {
    return(<>
        <Parent>
            <Child />
        </Parent>
    </>)
}

可以看到当点击5次后,子组件仅仅初始输出一次,并未re-rend。 2.png

但是这种子组件和父组件无传参的情况还是少见,对于有传参的情况,我们如何处理,随之而来:

2. 如何避免有依赖传参时,子组件的渲染

首先我们看看有依赖传参的情况。

function Child ({ tips }) {
    console.log("[child] re-rend:", tips);
    return (<div>我是儿砸。{tips}</div>)
}
function Parent({children}) {
    const [count, setCount] = useState(0);
    console.log('[parent] count:', count)
    return (<>
        {count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
        <Child tips={count % 3 === 0? '无余数': '有余数'} />
    </>)
}

同样是点击了5次, 可以从下图看到,子组件有相同的传参时(tips='有余数')仍然re-rend了。 image.png

我们如何避免上面这种情况呢—————使用React.memo

const Child = React.memo(({ tips }) => {
    console.log("[child] re-rend:", tips);
    return <div>我是儿砸。{tips}</div>;
})
function Parent({children}) {
    const [count, setCount] = useState(0);
    console.log('[parent] count:', count)
    return (<>
        {count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
        <Child tips={count % 3 === 0? '无余数': '有余数'} />
    </>)
}

可以看到当有相同的传参时,re-rend已经被避免了。

image.png

为何memo能达到这种效果能,我们可以从官网找到答案:

如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,'React' 将跳过渲染组件的操作并直接复用最近一次渲染的结果。—— 官网React.memo

但是得注意:使用React.memo对props的比较是浅比较

如果要进行自定义比较函数,可如下操作:

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);

三。总结

今天讨论了两种避免子组件重复渲染的方式。若有问题欢迎大家讨论和分享哈。