“React Memo 大揭秘:如何彻底改变你的应用性能!“ - 从 memo 来看 react 更新

链接

上篇文章,有一个问题,每一次点击 add ,2个子组件的打印都会打印,但是 useRef 的子组件的 props 并没有更新, 理论上来说是不需要更新的。那么应该如何优化呢,使得点击 add 后只有 countRef.current 的值变了,countRef.current 的子组件才更新。

答案就是使用 memo

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

const hasOwnProperty = Object.prototype.hasOwnProperty;

function is(x: any, y: any) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
  );
}

// react 源码中的比较函数
function shallowEqual(objA: any, objB: any): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== "object" ||
    objA === null ||
    typeof objB !== "object" ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

const Children = ({ name, value }: { name: string; value: number }) => {
  // 每一次子组件更新时,都会触发
  console.log(name, ":", value, "render");
  return (
    <div>
      {name}-Children:{value}
    </div>
  );
};

// 每一次更新都比较前后的 props 是否相同,如果返回 true 就不渲染,返回 false 才渲染。
const MemoChildren = memo(Children, (prevProps, currProps) => {
  console.log(
    prevProps,
    currProps,
    "equal:",
    shallowEqual(prevProps, currProps)
  );
  return shallowEqual(prevProps, currProps);
});

const CounterWithRef = () => {
  // 使用 useState 来管理计数状态
  const [count, setCount] = useState(0);

  // 使用 useRef 来存储计数的持久性引用,不会导致重新渲染
  const countRef = useRef(0);

  // 每一次页面更新时,都会触发
  console.log("count:", count, "countRef.current:", countRef.current, "render");

  const handleAdd = () => {
    // 使用 useState 更新状态,并触发重新渲染
    setCount((prevCount) => prevCount + 1);
  };

  const handleRefAdd = () => {
    // 使用 useRef 直接更新引用,不触发重新渲染
    countRef.current = countRef.current + 1;
  };

  const handleCheck = () => {
    // 用于实时查看 useState 和 useRef 的值
    console.log(
      "count:",
      count,
      "countRef.current:",
      countRef.current,
      "check"
    );
  };

  return (
    <div>
      <div style={{ display: "flex", gap: "6px" }}>
        <p>Count (useState): {count}</p>
        <button type="button" onClick={handleAdd}>
          add
        </button>
      </div>
      <div style={{ display: "flex", gap: "6px" }}>
        <p>Count (useRef): {countRef.current}</p>
        <button onClick={handleRefAdd}>refAdd</button>
      </div>
      <div onClick={handleCheck}>check</div>
      <MemoChildren name="useState" value={count}></MemoChildren>
      <MemoChildren name="useRef" value={countRef.current}></MemoChildren>
    </div>
  );
};

export default CounterWithRef;

子组件现在都被 memo 包装了一层,现在点击 add,可以看见比较的函数每一次都会触发,只有当 equal 为 false 时,组件的打印才会触发,也就代表了只有 props 更新了,子组件才更新。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值