React18

149 阅读1分钟

React 18

Concurrent Rendering(并发渲染)

1.渲染是可中断的

Suspense

<Suspense fallback={<Spinner />}>
  <Comments />
</Suspense>

SuspenseList

revealOrder: Suspense加载顺序

1.together: 所有Suspense一起显示,也就是最后一个加载完了才一起显示全部

2.forwards: 按照顺序显示Suspense

3.backwards: 反序显示Suspense

tail: 是否显示fallback,只在revealOrder为forwards或者backwards时候有效

1.hidden: 不显示

2.collapsed: 轮到自己再显示

import {useState, Suspense, SuspenseList} from "react"
import User from "../components/User"
import Num from "../components/Num"
import {fetchData} from "../utils"
import ErrorBoundaryPage from "./ErrorBoundaryPage"

const initialResource = fetchData();

export default function SuspenseListPage(props) {
  const [resource, setResource] = useState(initialResource);

  return (
    <div>
      <h3>SuspenseListPage</h3>
      <SuspenseList tail="collapsed">
        <ErrorBoundaryPage fallback={<h1>网络出错了</h1>}>
          <Suspense fallback={<h1>loading - user</h1>}>
            <User resource={resource} />
          </Suspense>
        </ErrorBoundaryPage>

        <Suspense fallback={<h1>loading-num</h1>}>
          <Num resource={resource} />
        </Suspense>
      </SuspenseList>

      <button onClick={() => setResource(fetchData())}>refresh</button>
    </div>
  )
}

Automatic Batching(自动批量处理)

// 以前: 这里的两次setState并没有批量处理,React会render两次
setTimeout(() => {
  setCount(c => c + 1)
  setFlag(f => !f)
}, 1000)

// React18: 自动批量处理,这里只会render一次
setTimeout(() => {
  setCount(c => c + 1)
  setFlag(f => !f)
}, 1000)

Transition(过渡)

紧急更新(Urgent updates): 跟用户交互的更新属于紧急更新

过渡更新(Transition updates): 跟界面呈现相关的更新属于过渡更新

startTransition

import {useEffect, useState, Suspense} from "react";
import Button from "../components/Button";
import User from "../components/User";
import Num from "../components/Num";
import {fetchData} from "../utils";

const initialResource = fetchData();

export default function TransitionPage(props) {
  const [resource, setResource] = useState(initialResource);

  // useEffect(() => {
  //   console.log("resource", resource); //sy-log
  // }, [resource]);

  return (
    <div>
      <h3>TransitionPage</h3>
      <Suspense fallback={<h1>loading - user</h1>}>
        <User resource={resource} />
      </Suspense>

      <Suspense fallback={<h1>loading-num</h1>}>
        <Num resource={resource} />
      </Suspense>

      <Button
        refresh={() => {
          setResource(fetchData());
        }}
      />
    </div>
  );
}
import {
  //startTransition,
  useTransition,
} from "react";

export default function Button({refresh}) {
  const [isPending, startTransition] = useTransition();

  return (
    <div className="border">
      <h3>Button</h3>
      <button
        onClick={() => {
          startTransition(() => {
            refresh();
          });
        }}
        disabled={isPending}>
        点击刷新数据
      </button>
      {isPending ? <div>loading...</div> : null}
    </div>
  );
}

useTransition

const [isPending, startTransition] = useTransition();

function handleClick() {
  startTransition(() => {
    setTab('comments');
  });
}

<Suspense fallback={<Spinner />}>
  <div style={{ opacity: isPending ? 0.8 : 1 }}>
    {tab === 'photos' ? <Photos /> : <Comments />}
  </div>
</Suspense>

useDeferredValue

import {useDeferredValue, useState} from "react";
import MySlowList from "../components/MySlowList";

export default function UseDeferredValuePage(props) {
  const [text, setText] = useState("hello");
  const deferredText = useDeferredValue(text);

  const handleChange = (e) => {
    setText(e.target.value);
  };
  return (
    <div>
      <h3>UseDeferredValuePage</h3>
      {/* 保持将当前文本传递给 input */}
      <input value={text} onChange={handleChange} />
      {/* 但在必要时可以将列表“延后” */}
      <p>{deferredText}</p>

      <MySlowList text={deferredText} />
    </div>
  );
}

useId

useSyncExternalStore

useInsertionEffect

原理

const HooksDispatcherOnMount = {
  useCallback: mountCallback,
  useEffect: mountEffect,
  useLayoutEffect: mountLayoutEffect,
  useMemo: mountMemo,
  useReducer: mountReducer,
  useRef: mountRef,
  useState: mountState,
}

const HooksDispatcherOnUpdate = {
  useCallback: updateCallback,
  useEffect: updateEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: updateReducer,
  useRef: updateRef,
  useState: updateState
}