react.memo、useMemo、useCallback深入理解

2023-01-11 20:35:49 浏览数 (1)

memo

memo和类组件的pureComponent效果一样,使被包裹的组件传入props有更新的时候,才会重新渲染

useMemo

useMemo的作用是缓存一个值,阻止它被react重新render,只有当依赖项改变的时候值才会更新

useMemo第一个参数是个函数,且必须有返回值(被缓存的值),第二个参数是数组,里面放被监听的变量(依赖项),有变量改变时,值才会被更新。

示例

代码语言:javascript复制
1import { useEffect, useMemo, useState } from "react";
2
3const UseMemoDemo = () => {
4  // 调用这个函数需要大量时间去计算
5  const slowFunction = (number: any) => {
6    console.log("calling slow function");
7    for (let i = 0; i <= 100000; i  ) {
8      console.log(i);
9    }
10    return number * 2;
11  };
12  const [inputNumber, setInputNumber] = useState(1);
13  const [dark, setDark] = useState(true);
14
15  // 场景1:执行某函数需要大量时间,使用useMemo来优化,在不必要执行函数的时候不执行函数
16  const doubleNumber = useMemo(() => slowFunction(inputNumber), [inputNumber]);
17
18  // 场景2:每次组件更新会重新执行,内部的引用类型变量会重新创建,这会导致使用到引用类型变量的组件重新渲染,使用useMemo来让每次的变量相同
19  const themeStyle = useMemo(() => {
20    return {
21      background: dark ? "black" : "white",
22      color: dark ? "white" : "black",
23    };
24  }, [dark]);
25
26  useEffect(() => {
27    console.log("themeStyle changed");
28  }, [themeStyle]);
29  const handleChange = (e: any) => {
30    setInputNumber(parseInt(e.target.value));
31  };
32  return (
33    <>
34      <input type="text" value={inputNumber} onChange={handleChange} />
35      <button
36        onClick={() => {
37          setDark((prevDark) => !prevDark);
38        }}
39      >
40        change theme
41      </button>
42      <p style={themeStyle}>{doubleNumber}</p>
43    </>
44  );
45};
46export default UseMemoDemo;
47

useCallback

useCallback的作用是缓存一个函数,阻止它被react重新render,只有当依赖项改变的时候值才会更新

useMemo第一个参数一个函数(被缓存的函数),第二个参数是数组,里面放被监听的变量(依赖项),有变量改变时,函数才会被更新。

示例

代码语言:javascript复制
1import  { useState, useCallback,memo } from "react";
2
3// 父组件,给子组件传递name和changeName方法
4const Parent = () => {
5  const [count, setCount] = useState(1);
6  const [name, setName] = useState("bbz");
7  const addCount = () => {
8    setCount(count   1);
9  };
10  
11  const changeName = useCallback((n) => {
12    setName(n);
13  }, []);
14
15
16  return (
17    <>
18      <div onClick={addCount}>计数: {count}</div>
19      <Child name={name} changeName={changeName} />
20    </>
21  );
22};
23
24// 子组件
25const Child =memo( ({ name, changeName }) => {
26  console.log("child start---");
27  return (
28    <div
29      onClick={() => {
30        changeName("bobozai");
31      }}
32    >
33      child comps: {name}
34    </div>
35  );
36})
37
38export default Parent;
39
40
41// 如果不使用useCallback,则点击父组件计数的同时,子组件会重新渲染(console了),显然这是没必要的
42// 因为更新count时父组件会重新渲染,导致重新生成了一个changeName函数,
43// 所以子组件的props变了,导致子组件会重新渲染
44// 而对changeName函数用useCallback进行包裹,则对函数进行缓存不会重新生成

适用场景

1、在组件内部,如果有变量的计算方式比较复杂,性能消耗较大,使用useMemo或useCallback进行数据存储,从而节约一些性能。

2、父组件更新,子组件没必要重新渲染的时候。子组件用memo包裹,父组件将传给子组件的参数用hooks缓存。,就可避免

共同优点

两个hooks缓存的值或者函数,会被react放进缓存区,当react组件由于state或者props改变而重新渲染时,组件内部定义的变量或者函数也会随之被重新计算生成。而被useMemo或者useCallback包裹后,只有当依赖项有变化时才会重新计算,否则react会直接从缓存区里取出来。以此可以节约一些react的性能,避免页面重新渲染时不必要的重复更新。

共同缺点

1、放进缓存区,也就等于产生闭包。闭包过多也会占用内存。 2、一直监听依赖项,也需要花费一些性能

心得

react官方给的建议是少用

如果变量或者函数的计算工序比较复杂,性能消耗较大,或者需要作为参数传给子组件,导致子组件无用更新了,使用这俩hooks优化,可以有效提升性能。

如果只是一个很简单的计算,不建议使用这俩hooks。因为性能优化带来的好处可能抵消不了它的成本、 就像你开车去10公⾥以外的⼩镇 和 你开车去隔壁的邻居家。酌情使用

0 人点赞