【React Hooks优化】减少重复渲染


前言

事情发生在一个下午,我需要用React hooks写一个定时器,因为useEffect每次执行都会使组件重新渲染一次,我发现如果在根组件使用定时器的话,根组件里所有的子组件也都会跟着渲染一次,哪怕它和变化的数据没有任何关系,也会被渲染。

这个时候我就在想。这样如果有大量的组件在频繁的做无意义的渲染,不是会大大增加浏览器的负担吗?我们能不能让不必要的组件不用渲染呢?

带着这个问题,我开始探索react中的优化…

一、为什么要进行优化?

React的默认渲染行为

初始化渲染
在初始化渲染时,我们需要渲染整个应用
(绿色 = 已渲染节点)

在这里插入图片描述
更新时渲染

我们想更新一部分数据。这些改变只和一个叶子节点相关.理想状态下是这样的.我们只想渲染通向叶子节点的关键路径上的这几个节点

在这里插入图片描述
默认行为:

(橘黄色 = 浪费的渲染)

在这里插入图片描述
由此可以看出,当我们每次渲染时,有些没有依赖值的组件也会造成渲染。

所以这就好造成浪费,下面让我们来看看怎么解决此类问题把!

二、使用memo/useMemo缓存组件

根组件:

import React,{useState,useEffect} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'

let timer = null
const App = () => {
  const [count,setCount] = useState(12)
  useEffect(() => {
    timer = setInterval(() => {
      setCount(n => {
        if (n) { // 3.1 倒计时每秒减少1
          return n - 1
        } else { // 3.2 倒计时为0时,清空倒计时
          clearInterval(timer)
          return 0
        }
      });
    }, 1000)
    // 4 组件销毁 清楚倒计时
    return ()=>{
      clearInterval(timer)
    }
  }, [])
  const [num,setNum] = useState(1)
  return (
    <>
      <Test1 count={count}/>
      <Test2 onClick={() => setNum(2)} num={num}/>
    </>
  )
}

export default App

子组件Test1:

import React from 'react'

const test1 = (props) => {
  const {count} = props
  console.log('test1',count);
  return (
    <div>
      
    </div>
  )
}

export default test1

子组件Test2:

import React from 'react'

const test2 = (props) => {
  const {num} = props
  console.log('test2',num);
  return (
    <div>
      
    </div>
  )
}

export default test2

在这里插入图片描述
可以看到我们的子组件随着count变化会被不停的渲染,下面让我们来看看怎么让那些无关的吃瓜组件不凑热闹一起渲染!

1.memo

memo包裹的组件在渲染时会对props做一次浅层次的对比,防止子组件出现额外的渲染

如果props没发生变化,就不会渲染

我们给Test2用memo包裹:

import React,{memo} from 'react'

const test2 = (props) => {
  const {num} = props
  console.log('test2',num);
  return (
    <div>
      
    </div>
  )
}

export default memo(test2)

在这里插入图片描述
我们发现Test2不会每次都跟着渲染了。

注意!!根组件给被包裹的子组件添加事件,会导致memo失效,它依旧会继续渲染

2.useMemo

使用useMemo缓存一个组件,相对于memo的优点:

  • 更方便。React.memo 需要对组件进行一次包装,生成新的组件。而 useMemo 只需在存在性能瓶颈的地方使用,不用修改组件。
  • 更灵活。useMemo 不用考虑组件的所有 Props,而只需考虑当前场景中用到的值,也可使用 useDeepCompareMemo 对用到的值进行深比较。
  • 绑定事件并不会使useMemo失效

让我们来看看怎样使用吧!

根组件:

import React,{useState,useEffect,useMemo} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'

let timer = null
const App = () => {
  const [count,setCount] = useState(12)
  useEffect(() => {
    timer = setInterval(() => {
      setCount(n => {
        if (n) { // 3.1 倒计时每秒减少1
          return n - 1
        } else { // 3.2 倒计时为0时,清空倒计时
          clearInterval(timer)
          return 0
        }
      });
    }, 1000)
    // 4 组件销毁 清楚倒计时
    return ()=>{
      clearInterval(timer)
    }
  }, [])
  const [num,setNum] = useState(1)
  const NewTest2 = useMemo(() => <Test2 onClick={() => setNum(2)} num={num}/>, [num])
  return (
    <>
      <Test1 count={count}/>
      {NewTest2}
    </>
  )
}

export default App

在这里插入图片描述
可以看到我们也生效了

2.useCallback

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

我们上面写法也能使用useCallback实现

import React,{useState,useEffect,useCallback} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'

let timer = null
const App = () => {
  const [count,setCount] = useState(12)
  useEffect(() => {
    timer = setInterval(() => {
      setCount(n => {
        if (n) { // 3.1 倒计时每秒减少1
          return n - 1
        } else { // 3.2 倒计时为0时,清空倒计时
          clearInterval(timer)
          return 0
        }
      });
    }, 1000)
    // 4 组件销毁 清楚倒计时
    return ()=>{
      clearInterval(timer)
    }
  }, [])
  const [num,setNum] = useState(1)
  // const NewTest2 = useMemo(() => <Test2 onClick={() => setNum(2)} num={num}/>, [num])
  const NewTest2 = useCallback(<Test2 onClick={() => setNum(2)} num={num}/>,[num])
  return (
    <>
      <Test1 count={count}/>
      {NewTest2}
    </>
  )
}

export default App

在这里插入图片描述
可以看到我们也是生效的哦

总结

react的优化手段还有很多,今天我们只是简单介绍了利用memo、useMemo和useCallback去减少重复渲染。

不积小流,无以成江海。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值