问题描述

在如下代码的情况下,如果我们点击按钮,会发现即使AB组件没有依赖timestamp,但每次setTimestamp也会让函数重新渲染,这其实是无意义的性能开销。


function A() {
  console.log('render A');
  return <>A</>;
}

function B() {
  console.log('render B');
  return <>B</>;
}

function App() {
  const [timestamp, setTimestamp] = useState(0);

  const onClick = () => {
    setTimestamp(Date.now());
  };

  console.log('render App');

  return (
    <>
      <button onClick={onClick}>setTimestamp</button>
      <A />
      <B />
    </>
  );
}

// 每次调用setTimestamp,都会输出
// render App
// render A
// render B

分析问题

那为什么会发生这种无意义的渲染呢?

其本质是react无法判断你当前更新的timestamp,是否跟子组件AB有某种内在联系(即使你明确的知道子组件没用使用它)。

所以react会假定所有的子组件都需要重新渲染,也就会导致了本文中的无意义的性能开销。

解决问题

方案1:使用React.memo包裹子组件。

当组件的props没有发生改变时,React.memo能够让组件会跳过渲染,规避了上面的问题。

const A = React.memo(function () {
  console.log('render A');
  return <>A</>;
});

const B = React.memo(function () {
  console.log('render B');
  return <>B</>;
});

function App() {
  const [timestamp, setTimestamp] = useState(0);

  const onClick = () => {
    setTimestamp(Date.now());
  };

  console.log('render App');

  return (
    <>
      <button onClick={onClick}>setTimestamp</button>
      <A />
      <B />
    </>
  );
}

// 每次调用setTimestamp,只会输出
// render App

方案2:将timestamp封装到独立的组件中。

这样重新渲染的范围就被限定到了Time的组件中,而不会导致其他外部组件的重新渲染。

function A() {
  console.log('render A');
  return <>A</>;
}

function B() {
  console.log('render B');
  return <>B</>;
}

function Time() {
  const [timestamp, setTimestamp] = useState(0);

  const onClick = () => {
    setTimestamp(Date.now());
  };

  console.log('render Time');

  return (
    <>
      <button onClick={onClick}>setTimestamp</button>
    </>
  );
}

function App() {
  console.log('render App');

  return (
    <>
      <Time />
      <A />
      <B />
    </>
  );
}

// 每次调用setTimestamp,只会输出
// render Time

总结问题

暂无


热饭班长
3.7k 声望434 粉丝

先去做,做出一坨狗屎,再改进。