亲宝软件园·资讯

展开

六分钟带你快速学会react中的useMemo

Breeze 人气:0

概念

react 中是通过一次次的 re-render (重新渲染)保持我们的值及时的更新到页面上的,每次重新渲染都是一次快照,可以把它想象成一张张的照片,在某个时刻它应该是什么样子的

useMemo

栗子

筛选偶数

以下代码实现功能:找出 0count 之间所有的偶数,count 可以动态改变,实时渲染在页面上,count 改变则会引起 re-render

import React, { useState } from 'react';

export default () => {
  const [count, setCount] = useState(100);

  const arr = [];
  for (let i = 0; i < count; i++) {
    if (i % 2 === 0) {
      arr.push(i);
    }
  }

  return (
    <>
      <form>
        <label htmlFor="num">Your number:</label>
        <input
          type="number"
          value={count}
          onChange={(event) => {
            // 设置最大值为 100000
            let num = Math.min(100_000, Number(event.target.value));
            setCount(num);
          }}
        />
      </form>
      <p>
        有{arr.length}偶数在 0 到 {count} 之间:<span>{arr.join(', ')}</span>
      </p>
    </>
  );
};

每秒获取时间刷新页面

下面代码增加了计时器,在页面显示实时的时间,这样页面每秒钟都会 re-render,也会每秒钟重新筛选一次偶数(尽管 count 并没有变化)

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

export default () => {
  const [count, setCount] = useState(100);
  const [curTime, setCurTime] = useState('');

  const useTime = () => {
    useEffect(() => {
      const intervalId = window.setInterval(() => {
        let time = new Date();
        setCurTime(time.toLocaleString());
      }, 1000);

      return () => {
        window.clearInterval(intervalId);
      };
    }, []);

    return curTime;
  };
  const time = useTime();

  const arr = [];
  for (let i = 0; i < count; i++) {
    if (i % 2 === 0) {
      arr.push(i);
    }
  }

  return (
    <>
      <form>
        <div>{time}</div>
        <label htmlFor="num">Your number:</label>
        <input
          type="number"
          value={count}
          onChange={(event) => {
            // 设置最大值为 100000
            let num = Math.min(100_000, Number(event.target.value));
            setCount(num);
          }}
        />
      </form>
      <p>
        有{arr.length}偶数在 0 到 {count} 之间:<span>{arr.join(', ')}</span>
      </p>
    </>
  );
};

如何优化

那我们需要的是啥,虽然每秒钟都在重新获取时间,但是我们的 count 如果并没有变化的话,我们没必要去一直重新计算它的,特别如果 count 的值特别大的时候,特别如果在一些旧设备上看着就会显得卡顿,极其影响性能 有了 useMemo 就是 so easy 啊

我们来改造下计算偶数地方的代码:这样如果 count 不变的情况下,不会进行重复的计算,直接使用上次的值

const arr = useMemo(() => {
  const temp = [];
  for (let i = 0; i < count; i++) {
    if (i % 2 === 0) {
      temp.push(i);
    }
  }
  return temp;
}, [count]);

useCallback、React.memo

既然讲到 useMemo 了,那么 useCallback、React.memo 也顺便说下吧,一个效果的东西,只不过将返回的值从对象变成了函数或者组件

下面看个小栗子:

// 父组件代码:一个计时器每秒更新时间,父组件每秒不停的 re-render,改变 count 值的 onCountChange 函数传入子组件
import React, { useState, useEffect, useCallback } from 'react';
import Child from './child';

export default () => {
  const [count, setCount] = useState(100);
  const [curTime, setCurTime] = useState('');

  const useTime = () => {
    useEffect(() => {
      const intervalId = window.setInterval(() => {
        let time = new Date();
        setCurTime(time.toLocaleString());
      }, 1000);

      return () => {
        window.clearInterval(intervalId);
      };
    }, []);

    return curTime;
  };
  const time = useTime();
  const onCountChange = () => {
    setCount((count) => count + 100);
  };
  console.log('re-render-father');

  return (
    <>
      <div>{time}</div>
      <div>{count}</div>
      <Child onCountChange={onCountChange} />
    </>
  );
};

// 子组件代码,接收 onCountChange 函数,并且用 React.memo 包裹函数
import React from 'react';

export default React.memo((props: any) => {
  const { onCountChange } = props;
  console.log('re-render-child');

  return (
    <>
      <div
        onClick={() => {
          onCountChange();
        }}
      >
        加100
      </div>
    </>
  );
});

分析一下上面的栗子:

原因:父组件在不停的 re-render 每次都会重新创建函数,在 js 中虽然两个函数一模一样,但是不是一个引用的话就不相等,所以 React.memo 在进行浅比较的时候就认为 props 变化了,子组件也会 re-render,造成了无效优化

解决办法:既然知道了原因所在,那我们如何解决呢,那就让它是同一个函数不就好了,那就用到了 useCallback 进行优化

const onCountChange = useCallback(() => {
  setCount((count) => count + 100);
}, []);

或者可以用 useMemo:返回变成函数即可

const onCountChange = useMemo(() => {
  return () => {
    setCount((count) => count + 100);
  };
}, []);

由此可见 useCallback 为 useMemo 的语法糖而已

将函数用 useCallback 包裹一样就会使用缓存的值,不会重新创建函数,也就不会重复 re-render 组件了

总结

加载全部内容

相关教程
猜你喜欢
用户评论