一文了解ReactHooks下的重复渲染类性能优化问题

前言

由于React自身的原因,父组件的状态改变时,即使子组件并不依赖该状态,他仍然会得到渲染,这就导致了性能问题,关于性能优化一直是区分小白React开发者和进阶开发者的重要利器。

Memo

提到性能优化,那不得不提到Hooks出来前,针对函数式组件发布的Memo,旨在解决前言中提到的问题。看一段代码:

const Child = (props) => {
    console.log(1)
    return(
        <div>1111111</div>
    );
}
const Parent = (props) => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>增加</button>
            <p>count:{count}</p>
            <Child />
        </>
    )
}

很明显,在我们Parent组件中操作按钮,即使与子组件没有任何关系,它也会不断更新。这时很简单我们只需要用Memo把子组件包裹上,这样他就会返回一个新的组件,当父组件发生状态改变时,如果子组件接受的参数并没有变化,那么子组件是不会重新渲染的。

很明显我们上面的例子,子组件根本没有接收参数,自然也不会产生性能优化问题。

虽然看似Memo很好用,实则也确实很有用,但是在一些情况下是没啥用的,简单的来说,如果在Hooks写法中,子组件需要传值,那么基本是没用的,问题就在于useSate这个东西,他每次得到的值都是变化的,即使数值没有变化。

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

const Child = props => {
  console.log(props.fixed);
  return <div>1111111</div>;
};

const ChildC = memo(Child);
const Parent = () => {
  const [count, setCount] = useState(0);
  const [fixed, setFixed] = useState(0);
  return (
    <>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        增加
      </button>
      <p>count:{count}</p>
      <ChildC fixed={{ fixed }} />
    </>
  );
};

export default Parent;

可以看到在这个例子中,由于父组件中点击事件的存在fixed每次都会被重新生成,而由于memo执行的浅比较,虽然只赋值fixed时安然无恙,但是当我们用引用类型包裹之后,memo即察觉出了些许问题,在没有数据更新的情况下强制刷新了子组件。

当然这里我也有个问题没有搞清楚,那就是如果我为fixed赋值引用类型,为什么直接传递fixed时子组件不刷新呢?

useCallback和useMemo

既然存在问题,Hooks就要解决嘛,那么useCallback和useMemo作为分别解决set函数和变量的两大方法,形成了新的优化运作方式。

import React, { useState, memo, useMemo, useCallback } from "react";

const Child = props => {
  console.log(props.fixed);
  return <div>1111111</div>;
};

const ChildC = memo(Child);
const Parent = () => {
  const [count, setCount] = useState(0);
  const [fixed, setFixed] = useState(0);
  return (
    <>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        增加
      </button>
      <p>count:{count}</p>
      <ChildC fixed={useMemo(() => ({ fixed }), [fixed])} />
    </>
  );
};

export default Parent;

关于useCallback就不举例了,类似。注意的是,如果后面不传依赖的话,即使与子组件相关的状态改变了,子组件也不会刷新。

同时我们也要注意滥用的问题,首先useMemo并不是无消耗的,反之它的性能消耗也不少,那么我们除非是进行高消耗的数据更新操作,不然反而是弄巧成拙。同样如上面所示,我们的传值如果仅仅是值类型,根本没必要用了。

总之工具虽好,也要分场合。

发布了346 篇原创文章 · 获赞 330 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43870742/article/details/103984720