导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.React18
    • 1.1 大纲
  • 2.创建项目
    • 2.1 安装依赖
    • 2.2 vite.config.js
    • 2.2 package.json
    • 2.3 index.html
    • 2.4 main.jsx
  • 3. 批量更新
    • 3.1 老的批量更新
      • 3.1.1 main.jsx
      • 3.1.2 OldBatchUpdatePage.jsx
      • 3.1.3 1.批量更新老实现.js
    • 3.2 新的批量更新
      • 3.2.1 main.jsx
      • 3.2.2 NewBatchUpdatePage.jsx
      • 3.2.3 2.批量更新新实现.js
  • 4. Suspense
    • 4.1 main.jsx
    • 4.2 SuspensePage.jsx
    • 4.3 ErrorBoundary.jsx
    • 4.4 fakeApi.jsx
    • 4.5 utils.jsx
  • 5. startTransition
    • 5.1 main.jsx
    • 5.2 StartTransitionPage.jsx
    • 5.3 UpdatePriorityPage.jsx
  • 6. useDeferredValue
    • 6.1 main.jsx
    • 6.2 src\routes\UseDeferredValuePage.jsx
  • 7. useTransition
    • 7.1 main.jsx
    • 7.2 UseTransitionPage.jsx
  • 9.基础知识
    • 9.1. 并发更新
    • 9.2 更新优先级
    • 9.3 双缓冲
    • 9.4 水合
  • 10. React18升级指南

1.React18 #

  • react-18
  • New Suspense SSR Architecture in React 18
  • Upgrading to React 18 on the server
  • hooks-reference

1.1 大纲 #

  • 批量更新
  • Suspense
  • startTransition
  • useDeferredValue
  • useTransition
  • Suspense SSR
  • 并发渲染
  • 更新优先级
  • 双缓冲
  • 水合
  • React18升级指南

2.创建项目 #

  • vitejs
  • plugin-react-refresh

2.1 安装依赖 #

npm install react  react-dom  @types/react @types/react-dom --save
npm install vite  @vitejs/plugin-react-refresh --save-dev

2.2 vite.config.js #

vite.config.js

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
export default defineConfig({
  plugins: [reactRefresh()]
})

2.2 package.json #

package.json

{
  "scripts": {
    "start": "vite"
  },
}

2.3 index.html #

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite App</title>
</head>

<body>
  <div id="root"></div>
  <script src="/src/main.jsx" type="module"></script>
</body>

</html>

2.4 main.jsx #

src\main.jsx

import React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
const element = <div>app</div>
render(element, root);
createRoot(root).render(element);

3. 批量更新 #

  • automatic batching
  • 在React中多次的setState合并到一次进行渲染
  • 在React18中更新是以优先级为依据进行合并的

3.1 老的批量更新 #

3.1.1 main.jsx #

src\main.jsx

import React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
+import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
+const element = <OldBatchUpdatePage />
render(element, root);
//createRoot(root).render(element);

3.1.2 OldBatchUpdatePage.jsx #

src\routes\OldBatchUpdatePage.jsx

import React, { Component } from 'react'
import { unstable_batchedUpdates } from 'react-dom'
class OldBatchUpdatePage extends Component {
  state = { number: 0 }
  handleCLick = () => {
    this.setState({ number: this.state.number + 1 });
    console.log("number", this.state.number);
    this.setState({ number: this.state.number + 1 });
    console.log("number", this.state.number);
    setTimeout(() => {
      //unstable_batchedUpdates(() => {
      this.setState({ number: this.state.number + 1 });
      console.log("number", this.state.number);
      this.setState({ number: this.state.number + 1 });
      console.log("number", this.state.number);
      //})
    }, 0);
  };

  render() {
    return (
      <div>
        <p>{this.state.number}</p>
        <button onClick={this.handleCLick}>+</button>
      </div>
    );
  }
}
export default OldBatchUpdatePage;

3.1.3 1.批量更新老实现.js #

let isBatchingUpdate = false;
let updateQueue = [];
let state = { number: 0 };
function setState(newState) {
  if (isBatchingUpdate) {
    updateQueue.push(newState);
  } else {
    state = newState;
  }
}
const handleCLick = () => {
  setState({ number: state.number + 1 });
  console.log("number", state.number);
  setState({ number: state.number + 1 });
  console.log("number", state.number);
  setTimeout(() => {
    setState({ number: state.number + 1 });
    console.log("number", state.number);
    setState({ number: state.number + 1 });
    console.log("number", state.number);
  }, 0);
};
function batchedUpdates(fn) {
  isBatchingUpdate = true;
  fn();
  isBatchingUpdate = false;
  updateQueue.forEach(newState => {
    state = newState
  });
}
batchedUpdates(handleCLick);

3.2 新的批量更新 #

3.2.1 main.jsx #

src\main.jsx

import React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
+import NewBatchUpdatePage from './routes/NewBatchUpdatePage';
+const element = <NewBatchUpdatePage />
render(element, root);
//createRoot(root).render(element);

3.2.2 NewBatchUpdatePage.jsx #

src\routes\NewBatchUpdatePage.jsx

import React, { Component } from 'react'
import { flushSync } from 'react-dom'
class NewBatchUpdatePage extends Component {
  state = { number: 0 }
  handleCLick = () => {
    this.setState({ number: this.state.number + 1 });
    console.log("number", this.state.number);
    this.setState({ number: this.state.number + 1 });
    console.log("number", this.state.number);
    setTimeout(() => {
      flushSync(() => {
        this.setState({ number: this.state.number + 1 });
      });
      console.log("number", this.state.number);
      flushSync(() => {
        this.setState({ number: this.state.number + 1 });
      });
      console.log("number", this.state.number);
    }, 0);
  };

  render() {
    return (
      <div>
        <p>{this.state.number}</p>
        <button onClick={this.handleCLick}>+</button>
      </div>
    );
  }
}
export default NewBatchUpdatePage

3.2.3 2.批量更新新实现.js #

let updateQueue = [];
let lastPriority = -1;
let state = { number: 0 };
const InputPriority = 1;
const NormalPriority = 1;
let lastUpdatePriority;

function setState(newState, priority) {
  updateQueue.push(newState);
  if (lastUpdatePriority === priority) {
    return;
  }
  lastUpdatePriority = priority;
  setTimeout(() => {
    updateQueue.forEach(newState => {
      state = newState
    });
  });
}
function flushSync(fn) {
  lastUpdatePriority = null;
  fn();
}

const handleCLick = () => {
  setState({ number: state.number + 1 }, InputPriority);
  console.log("number", state.number);
  setState({ number: state.number + 1 }, InputPriority);
  console.log("number", state.number);
  setTimeout(() => {
    setState({ number: state.number + 1 }, NormalPriority);
    console.log("number", state.number);
    setState({ number: state.number + 1 }, NormalPriority);
    console.log("number", state.number);
  }, 500);
};
handleCLick();

4. Suspense #

  • Suspense 让你的组件在渲染之前进行等待,并在等待时显示fallback的内容
  • Suspense内的组件子树比组件树的其他部分拥有更低的优先级
  • 执行流程
    • 在render函数中我们可以使用异步请求数据
    • react会从我们缓存中读取这个缓存
    • 如果没有缓存,那么会抛出一个promise异常
    • 当这个promise完成后(比发请求数据完成),react会继续回到原来的render中,把数据render出来
  • 完全同步写法,没有任何异步callback之类的东西
  • ErrorBoundary(错误边界)是一个组件,该组件会捕获到渲染期间(render)子组件发生的错误,并有能力阻止错误继续传播
  • 官方demo

4.1 main.jsx #

src\main.jsx

import React from 'react'
-import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
import NewBatchUpdatePage from './routes/NewBatchUpdatePage';
+import SuspensePage from './routes/SuspensePage';
+const element = <SuspensePage />
-render(element, root);
createRoot(root).render(element);

4.2 SuspensePage.jsx #

src\routes\SuspensePage.jsx

import React, { Component, Suspense } from 'react'
import ErrorBoundary from './components/ErrorBoundary';
import { fetchUser } from '../fakeApi';
import { wrapPromise } from '../utils';
const userPromise = fetchUser('1');
const userResource = wrapPromise(userPromise);
class SuspensePage extends Component {
  render() {
    return (
      <Suspense fallback={<h3>Loading User......</h3>}>
        <ErrorBoundary>
          <User />
        </ErrorBoundary>
      </Suspense >
    );
  }
}
function User() {
  const user = userResource.read();
  return <div>{user.id}:{user.name}</div>
}

export default SuspensePage;

4.3 ErrorBoundary.jsx #

src\routes\components\ErrorBoundary.jsx

import React from 'react'
class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  static getDerivedStateFromError(error) {
    return {
      hasError: true,
      error
    };
  }
  render() {
    if (this.state.hasError) {
      return (
        <>
          {this.state.error.msg}
        </>
      );
    }
    return this.props.children;
  }
}
export default ErrorBoundary

4.4 fakeApi.jsx #

src\fakeApi.jsx

export function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id, name: `姓名${id}` });
      //reject({ msg: '获取数据失败' });
    }, 1000 * Number(id));
  });
}

4.5 utils.jsx #

src\utils.jsx

export function wrapPromise(promise) {
  let status = "pending";
  let result;
  let suspender = promise.then(
    (r) => {
      status = "success";
      result = r;
    },
    (e) => {
      status = "error";
      result = e;
    }
  );
  return {
    read() {
      if (status === "pending") {
        throw suspender;
      } else if (status === "error") {
        throw result;
      } else if (status === "success") {
        return result;
      }
    }
  };
}

5. startTransition #

  • startTransition主要为了能在大量的任务下也能保持 UI 响应
  • startTransition可以通过将特定更新标记为过渡来显着改善用户交互

5.1 main.jsx #

src\main.jsx

import React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
import NewBatchUpdatePage from './routes/NewBatchUpdatePage';
import SuspensePage from './routes/SuspensePage';
+import StartTransitionPage from './routes/StartTransitionPage';
+const element = <StartTransitionPage />
//render(element, root);
createRoot(root).render(element);

5.2 StartTransitionPage.jsx #

src\routes\StartTransitionPage.jsx

import React, { startTransition, useEffect, useState } from 'react';
function getSuggestions(keyword) {
  let items = new Array(10000).fill(keyword)
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(items);
    }, 1000 * keyword.length);
  });
}

function Suggestion(props) {
  const [suggestions, setSuggestions] = useState([]);
  useEffect(() => {
    if (props.keyword && props.keyword.length > 0) {
      getSuggestions(props.keyword).then(suggestions => {
        startTransition(() => {
          setSuggestions(suggestions);
        })
      })
    }
  }, [props.keyword]);
  useEffect(() => {
    console.log(props.keyword);
  })
  return (
    <ul>
      {
        suggestions.map((item, index) => (<li key={index}>{item}</li>))
      }
    </ul>
  )
}
function StartTransitionPage() {
  const [keyword, setKeyword] = useState < string > ("");
  const handleChange = (event) => {
    setKeyword(event.target.value);
  };
  return (
    <div>
      <div>关键字<input value={keyword} onChange={handleChange} /></div>
      <Suggestion keyword={keyword} />
    </div>
  );
}
export default StartTransitionPage

5.3 UpdatePriorityPage.jsx #

UpdatePriorityPage.js

import React, { startTransition, useEffect } from 'react'
import { flushSync } from 'react-dom';
function UpdatePriorityPage() {
  let [state, setState] = React.useState('');
  useEffect(() => {
    console.log(state);
  });
  const update = () => {
    flushSync(() => startTransition(() => setState(state => state + 'A')));
    flushSync(setState(state => state + 'B'));
    flushSync(() => startTransition(() => setState(state => state + 'C')));
    flushSync(setState(state => state + 'D'));
  }
  return (
    <>
      <div>{state}</div>
      <button onClick={update}>更新</button>
    </>
  )
}
export default UpdatePriorityPage;

6. useDeferredValue #

  • 如果说某些渲染比较消耗性能,比如存在实时计算和反馈,我们可以使用这个useDeferredValue降低其计算的优先级,使得避免整个应用变得卡顿

6.1 main.jsx #

src\main.jsx

import React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
import NewBatchUpdatePage from './routes/NewBatchUpdatePage';
import SuspensePage from './routes/SuspensePage';
import StartTransitionPage from './routes/StartTransitionPage';
+import UseDeferredValuePage from './routes/UseDeferredValuePage';
+const element = <UseDeferredValuePage />
createRoot(root).render(element);

6.2 src\routes\UseDeferredValuePage.jsx #

src\routes\UseDeferredValuePage.jsx

import React, { useDeferredValue, useEffect, useState } from 'react';
function getSuggestions(keyword) {
  let items = new Array(10000).fill(0).map((item, index) => keyword + index);
  return Promise.resolve(items);
}

function Suggestion(props) {
  const [suggestions, setSuggestions] = useState < Array < string >> ([]);
  useEffect(() => {
    getSuggestions(props.keyword).then(suggestions => {
-      startTransition(() => {
         setSuggestions(suggestions);
-      })
    })
  }, [props.keyword]);

  return (
    <ul>
      {
        suggestions.map((item) => (<li key={item}>{item}</li>))
      }
    </ul>
  )
}
function StartTransitionPage() {
  const [keyword, setKeyword] = useState < string > ("");
+ const deferredText = useDeferredValue(keyword);
  const handleChange = (event) => {
    setKeyword(event.target.value);
  };
  return (
    <div>
      <div>关键字<input value={keyword} onChange={handleChange} /></div>
+     <Suggestion keyword={deferredText} />
    </div>
  );
}
export default StartTransitionPage

7. useTransition #

  • useTransition允许组件在切换到下一个界面之前等待内容加载,从而避免不必要的加载状态
  • useTransition 返回两个值的数组
    • startTransition 是一个接受回调的函数,我们用它来告诉 React 需要推迟的 state
    • isPending 是一个布尔值,这是 React 通知我们是否正在等待过渡的完成的方式

7.1 main.jsx #

src\main.jsx

import React from 'react'
import { createRoot } from 'react-dom/client'
const root = document.getElementById('root');
import OldBatchUpdatePage from './routes/OldBatchUpdatePage';
import NewBatchUpdatePage from './routes/NewBatchUpdatePage';
import SuspensePage from './routes/SuspensePage';
import StartTransitionPage from './routes/StartTransitionPage';
import UseDeferredValuePage from './routes/UseDeferredValuePage';
+import UseTransitionPage from './routes/UseTransitionPage';
+const element = <UseTransitionPage />
createRoot(root).render(element);

7.2 UseTransitionPage.jsx #

src\routes\UseTransitionPage.jsx

import React, { Suspense, useState, useTransition } from 'react'
import ErrorBoundary from './components/ErrorBoundary';
import { fetchUser } from '../fakeApi';
import { wrapPromise } from '../utils';
const user1Resource = wrapPromise(fetchUser('1'));
const user5Resource = wrapPromise(fetchUser('5'));
function UseTransitionPage() {
  const [resource, setResource] = useState(user1Resource);
  const [isPending, startTransition] = useTransition();
  return (
    <>
      <Suspense fallback={<h3>Loading User......</h3>}>
        <ErrorBoundary>
          <User resource={resource} />
        </ErrorBoundary>
      </Suspense>
      <button onClick={() => {
        startTransition(() => {
          setResource(user5Resource)
        });
      }}>user5</button>
      <h3>{isPending && <p>isPending...</p>}</h3>
    </>
  );
}
function User({ resource }) {
  const user = resource.read();
  return <div>{user.id}:{user.name}</div>
}
export default UseTransitionPage;

9.基础知识 #

9.1. 并发更新 #

  • What happened to concurrent "mode"?
  • 并发更新就是一种可中断渲染的架构
  • 什么时候中断渲染呢?当一个更高优先级渲染到来时,通过放弃当前的渲染,立即执行更高优先级的渲染,换来视觉上更快的响应速度
  • 在React18中以是否使用并发特性作为是否开启并发更新的依据

9.2 更新优先级 #

  • 以前更新没有优先级的概念,优先级高的更新并不能打断之前的更新,需要等前面的更新完成后才能进行
  • 用户对不同的操作对交互的执行速度有不同的预期,所以我们可以根据用户的预期赋予更新不同的优先级
    • 高优先级 用户输入、窗口缩放和拖拽事件等
    • 低优先级 数据请求和下载文件等
  • 高优先级的更新会中断正在进行的低优先级的更新
  • 等高优先级更新完成后,低优先级基于高优先级更新的结果重新更新
  • 对于 CPU-bound 的更新 (例如创建新的 DOM 节点和运行组件中的代码),并发意味着一个更急迫的更新可以中断已经开始的渲染

jia_sai_1625651545473

9.3 双缓冲 #

  • 当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图
  • 双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度

shuang_huan_cun_hui_tu

9.4 水合 #

  • 水合反应( hydrated reaction),也叫作水化
  • 是指物质溶解在水里时,与水发生的化学作用,水合分子的过程
  • 组件在服务器端拉取数据(水),并在服务器端首次渲染
  • 脱水: 对组件进行脱水,变成HTML字符串,脱去动态数据,成为风干标本快照
  • 注水:发送到客户端后,重新注入数据(水),重新变成可交互组件




10. React18升级指南 #

  • 18.0.0
  • How to Upgrade to React 18

访问验证

请输入访问令牌

Token不正确,请重新输入