阅读 React 官方文档的部分笔记。

Suspense

<Suspense> 展示一个备用组件(一般是 Loading),直到子组件完成加载。

<Suspense fallback={<Loading />}>
  <SomeComponent />
</Suspense>

Props

  • children:实际要渲染的 UI,如果 children 正在渲染,那么 Suspense 就会渲染 fallback
  • fallback:如果实际上要渲染的 UI 还没完全加载,则渲染一个替代的 UI 来代替它。

注意事项

  • React 不会为那些在首次加载前被暂停的渲染保留任何状态。当组件加载完毕后,React 会重新尝试从头开始渲染被暂停的树。
  • 如果 Suspense 正在显示内容,但又被暂停了,那么它会重新显示成 fallback。除非由 startTransitionuseDeferredValue 引发的更新。
  • Suspense 的再次隐藏复现,会触发布局副作用。

内嵌组件的函数定义

在组件内部定义另一个函数组件,这是一个不正确的做法,因为每次组件重新渲染的时候,内部定义的组件会重新初始化。

import { useState } from 'react';

export default function MyComponent() {
  const [counter, setCounter] = useState(0);

  function MyTextField() {
    const [text, setText] = useState('');

    return <input value={text} onChange={(e) => setText(e.target.value)} />;
  }

  return (
    <>
      <MyTextField />
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        Clicked {counter} times
      </button>
    </>
  );
}

上面是来自官方的例子:xenodochial-colden-957ggf

当我们点击按钮时,组件重新渲染更新,MyTextField 函数会被重新创建一次,只是每次在相同的位置创建了不同的实例组件。

为避免该问题,总是在顶层定义函数,切勿在函数内部嵌定义函数。

重置状态在相同的位置

在读取文档的时候还发现了一件有意思的事情。

在默认情况下,当一个组件出现在相同的位置时,React 保持组件原有的状态。但有时会出现一些意外情况。

import { useState } from 'react';

export default function Scoreboard() {
  const [isPlayerA, setIsPlayerA] = useState(true);
  return (
    <div>
      {isPlayerA ? <Counter person="Taylor" /> : <Counter person="Sarah" />}
      <button
        onClick={() => {
          setIsPlayerA(!isPlayerA);
        }}
      >
        Next player!
      </button>
    </div>
  );
}

function Counter({ person }) {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);

  let className = 'counter';
  if (hover) {
    className += ' hover';
  }

  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>
        {person}'s score: {score}
      </h1>
      <button onClick={() => setScore(score + 1)}>Add one</button>
    </div>
  );
}

以上是官方例子:reverent-surf-ly5h9z

该问题主要是当我们切换玩家的时候,分数还会保持原有的状态。因为 React 会将相同位置的 Counter 组件视为同一个,仅仅是 person 的 prop 进行了变动。

有两个方法可以解决以上问题:

  • 在不同的位置渲染组件。
  • 给每个组件加上显式的 key

第一种方法就是 {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> }

第二种方法就是 {isPlayerA ? <Counter key="Taylor" person="Taylor" /> : <Counter key="Sarah" person="Sarah" />}

参考链接