升级准备
安装新版本
新 JSX 转换:React 19 要求启用新的 JSX 转换,以改进包体积并允许在不显式导入 React 的情况下使用 JSX。如果未启用新转换,将会收到警告。大多数应用程序应该不会受到影响,因为该转换在大多数环境中已经启用。
安装命令:
npm install --save-exact react@rc react-dom@rc
或者使用 Yarn:
yarn add --exact react@rc react-dom@rc
TypeScript 更新:如果使用 TypeScript,还需更新类型定义。稳定版本发布后,可以从@types/react和@types/react-dom安装类型。
Codemods 工具
自动更新代码:为简化升级过程,提供了一系列 codemods 工具,帮助自动更新代码以符合 React 19 的新 API 和模式。推荐使用codemod命令运行这些工具,因为它运行更快,支持更复杂的代码迁移。
运行命令:
npx codemod@latest react/19/migration-recipe
此命令将运行多个常用的 codemods,例如替换react-dom.render、字符串 refs 等。
破坏性变更
错误处理改进
window.reportError报告,而捕获的错误则会记录到控制台。这一变化旨在减少重复的错误日志。移除已弃用 API
移除函数组件的propTypes和defaultProps:
propTypes检查将被静默忽略,建议迁移到 TypeScript 或其他类型检查解决方案。defaultProps。移除遗留上下文 API:
contextTypes和getChildContext被移除,建议迁移到新的contextType API。移除字符串 refs:字符串 refs 已被回调 refs 替代,使用字符串 refs 的组件需要迁移至回调形式。
移除模块模式工厂:模块模式工厂已被移除,需迁移至常规函数。
移除createFactory:不再支持此 API,需直接使用 JSX 替代。
移除测试工具中的一些 API:
react-dom/test-utils中的所有测试工具函数已被删除,仅保留了对act的支持,并且需要从react包中导入。npm create vite@latest
npm install
npm install --save-exact react@rc react-dom@rc
npm i express cors
npm run dev
use 允许你直接在组件中处理异步操作(例如 Promise 或异步函数),而无需显式管理状态、useEffect 或手动 .then/.catch。
它的目标是让组件更加声明式,直接表达“使用这个异步资源”而无需处理数据获取和状态管理的样板代码。
简化异步数据获取:
use 的参数。内置与 Suspense 集成:
const data = use(fetchData());
fetchData() 返回一个 Promise,use 会等待它解析为数据(或抛出错误)。Promise 未完成,React 会挂起组件,显示最接近的 Suspense fallback。更简单的代码:
useState 和 useEffect 手动管理异步状态。更好的可读性:
自动与 Suspense 配合:
对比传统方式
| 特性| 传统异步方式| use 使用方式|
|----|----|----|
| 数据获取| 需要 useEffect + useState 手动管理 | 直接 use(fetchData())|
| 错误处理| 需要手动捕获错误并更新状态| 自动触发 Suspense error boundary|
| Loading 状态管理| 需要显式设置 isLoading 状态| 自动由 Suspense fallback 管理|
| 数据流和逻辑耦合| 逻辑分散在多个 Hook 中| 数据获取逻辑集中在一个语句|
src\apps\App1.jsx
// 从 React 中导入 use hook
import { use } from "react";
// 创建一个立即解析为 'hello' 的 Promise
const getMessage = Promise.resolve("hello");
// 定义 App 组件
function App() {
// 使用 use hook 处理 Promise 并获取数据
const data = use(getMessage);
// 返回包含数据的 JSX
return <div>{data}</div>;
}
// 导出 App 组件
export default App;
src\apps\App2.jsx
//import { use } from 'react';
// 创建一个立即解析为 'hello' 的 Promise
const getMessage = Promise.resolve("hello");
// 定义 App 组件
function App() {
console.log("App 组件被渲染");
// 使用 use hook 处理 Promise 并获取数据
const data = use(getMessage);
// 返回包含数据的 JSX
return <div>{data}</div>;
}
// 导出 App 组件
export default App;
function use(promise) {
// 根据 promise 的状态进行不同处理
switch (promise.status) {
// 如果 promise 已完成,返回结果
case "fulfilled":
return promise.value;
// 如果 promise 已拒绝,抛出错误
case "rejected":
throw promise.reason;
// 默认情况下处理 pending 状态
default:
throw promise.then(
// 成功回调:设置状态为完成并保存结果
(value) => {
promise.status = "fulfilled";
promise.value = value;
},
// 失败回调:设置状态为拒绝并保存错误原因
(reason) => {
promise.status = "rejected";
promise.reason = reason;
}
);
}
}
src\apps\App10.jsx
// 从 React 中导入 use hook
import { use } from "react";
// 创建一个立即解析为 'hello' 的 Promise
const getMessage = Promise.resolve("hello");
// 创建一个立即解析为 'world' 的 Promise
const getAnotherMessage = Promise.resolve("world");
function App() {
// 定义一个布尔值条件
const condition = false;
// 根据条件使用不同的 Promise
const data = condition ? use(getMessage) : use(getAnotherMessage);
// 返回包含条件渲染结果的 JSX
return (
<div>
<p>条件渲染的结果: {data}</p>
{/* 当 condition 为 true 时才渲染此段落 */}
{condition && <p>条件为真时显示</p>}
</div>
);
}
export default App;
src\apps\App11.jsx
// 从 React 中导入 use hook
import { use } from "react";
// 创建一个包含三个立即解析的 Promise 的数组
const messages = [
Promise.resolve("hello"),
Promise.resolve("world"),
Promise.resolve("react"),
];
// 定义主应用组件
function App() {
// 使用 map 遍历 messages 数组,处理每个 Promise
const dataList = messages.map((promise, index) => {
// 使用 use hook 获取 Promise 的结果
const data = use(promise);
// 返回包含消息的 JSX 元素
return (
<div key={index}>
<p>
消息 {index + 1}: {data}
</p>
</div>
);
});
// 返回包含所有消息的容器
return <div>{dataList}</div>;
}
// 导出 App 组件
export default App;
src\apps\App3.jsx
// 从 React 中导入 Component 类
import { Component } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义错误边界组件
class ErrorBoundary extends Component {
// 构造函数,初始化状态
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
// 静态方法,用于从错误中派生新状态
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
// 渲染方法
render() {
// 如果有错误则显示错误信息
if (this.state.hasError) {
return <div>出错了: {this.state.error}</div>;
}
// 否则渲染子组件
return this.props.children;
}
}
// 为 ErrorBoundary 组件定义 PropTypes
ErrorBoundary.propTypes = {
children: PropTypes.node.isRequired,
};
// 创建一个会被拒绝的 Promise
const getMessage = Promise.reject("something went wrong");
// 定义主应用组件
function App() {
// 打印渲染信息
console.log("App 组件被渲染");
// 返回被 ErrorBoundary 包裹的 Message 组件
return (
<ErrorBoundary>
<Message />
</ErrorBoundary>
);
}
// 定义 Message 组件
function Message() {
// 使用 use hook 处理 Promise
const data = use(getMessage);
// 返回数据显示
return <div>{data}</div>;
}
// 导出 App 组件
export default App;
// 实现 use 函数
function use(promise) {
// 根据 promise 的状态进行不同处理
switch (promise.status) {
// 如果 promise 已完成,返回结果
case "fulfilled":
return promise.value;
// 如果 promise 已拒绝,抛出错误
case "rejected":
throw promise.reason;
// 默认情况下处理 pending 状态
default:
throw promise.then(
// 成功回调:设置状态为完成并保存结果
(value) => {
promise.status = "fulfilled";
promise.value = value;
},
// 失败回调:设置状态为拒绝并保存错误原因
(reason) => {
promise.status = "rejected";
promise.reason = reason;
}
);
}
}
在 React 的严格模式下,组件首次加载时会执行两次,这一行为是有意设计的,旨在帮助开发者发现潜在的错误和不纯的组件。
严格模式的目的 严格模式(Strict Mode) 是 React 提供的一种开发工具,用于识别不安全的生命周期方法、过时的 API 以及其他潜在的问题。它通过额外的检查来确保组件是纯函数,即给定相同的输入总是返回相同的输出。
双重渲染的实现 在严格模式下,React 会对每个组件进行双重渲染。这意味着在开发环境中,组件函数会被调用两次。这种设计有几个目的:
检测副作用:通过双重渲染,开发者可以更容易地发现组件在渲染过程中是否存在副作用(例如,修改外部状态)。
useEffect 钩子,React 会在第一次渲染时调用该钩子,并在第二次渲染时再次调用,以确保清理逻辑能够正常工作。src\apps\App4.jsx
// 导入 React 的 hooks
import { useEffect, useRef, useState } from "react";
// 定义 StrictMode 组件
function StrictMode() {
// 定义计数状态
const [count, setCount] = useState(0);
// 使用 ref 记录组件是否已加载
const hasLoaded = useRef(false);
// 使用 ref 记录组件渲染次数
const renderCount = useRef(0);
// 每次渲染时打印渲染次数
console.log(`组件渲染次数: ${++renderCount.current}`);
// 使用 useEffect 处理副作用
useEffect(() => {
// 打印 effect 执行次数
console.log(`Effect执行次数: ${count}`);
// 仅在首次加载时执行
if (!hasLoaded.current) {
console.log("仅首次加载执行一次");
hasLoaded.current = true;
}
// 返回清理函数
return () => {
console.log("Effect清理");
};
}, [count]);
// 渲染 UI
return (
<div>
{/* 显示当前计数 */}
<p>当前计数: {count}</p>
{/* 点击按钮增加计数 */}
<button onClick={() => setCount((c) => c + 1)}>增加</button>
</div>
);
}
// 导出组件
export default StrictMode;
Suspense 是 React 的一个强大的工具,用于处理异步操作,优化用户界面加载体验。它通过挂起组件渲染,直到数据或资源准备就绪,从而提升用户体验,尤其是在加载动态数据或代码拆分时。
Suspense 的工作机制是挂起组件的渲染,等待某些异步资源(如数据或代码)加载完成。加载期间,React 会渲染一个 fallback 指定的备用 UI。
<Suspense fallback={<LoadingComponent />}>
<YourComponent />
</Suspense>
fallback:指定在组件挂起期间显示的备用 UI,比如加载指示器。YourComponent:依赖异步资源的组件,可能通过数据加载或动态导入等方式延迟渲染。使用动态导入(React.lazy)按需加载组件。
src\apps\App5.jsx
// 从 React 中导入 Suspense 组件
import React, { Suspense } from "react";
// 使用 React.lazy 动态导入 LazyComponent 组件
const LazyComponent = React.lazy(() => import("../components/LazyComponent"));
// 定义主应用组件
function App() {
return (
// 使用 Suspense 包裹懒加载组件,并提供加载中的提示
<Suspense fallback={<p>Loading component...</p>}>
{/* 渲染懒加载的组件 */}
<LazyComponent />
</Suspense>
);
}
// 导出 App 组件
export default App;
src\components\LazyComponent.jsx
function LazyComponent() {
return (
<div>
<h1>这是一个延迟加载的组件</h1>
</div>
);
}
export default LazyComponent;
LazyComponent 加载完成前,渲染 <p>Loading component...</p>。使用 Suspense 等待图片或其他资源加载。
src\apps\App6.jsx
// 导入React的Suspense组件用于处理异步加载
import { Suspense } from "react";
// 导入PropTypes用于类型检查
import PropTypes from "prop-types";
// 图片加载组件,接收src属性
function ImageLoader({ src }) {
// 预加载图片并获取资源
const resource = preloadImage(src);
// 渲染图片,通过read()方法获取加载后的src
return <img src={resource.read()} alt="Loaded" />;
}
// 定义ImageLoader组件的属性类型
ImageLoader.propTypes = {
src: PropTypes.string.isRequired,
};
// 创建图片缓存Map
const imageCache = new Map();
// 图片预加载函数
function preloadImage(src) {
// 如果图片已在缓存中,直接返回缓存的资源
if (imageCache.has(src)) {
return imageCache.get(src);
}
// 初始化状态和结果变量
let status = "pending";
let result;
// 创建Promise来处理图片加载
const promise = new Promise((resolve, reject) => {
// 创建新的Image对象
const img = new Image();
img.src = src;
// 图片加载成功的处理函数
img.onload = () => {
status = "success";
result = src;
resolve(src);
};
// 图片加载失败的处理函数
img.onerror = () => {
status = "error";
result = new Error("Failed to load image");
reject(result);
};
});
// 创建资源对象
const resource = {
// read方法根据状态返回不同结果
read() {
if (status === "pending") throw promise;
if (status === "error") throw result;
return result;
},
};
// 将资源存入缓存
imageCache.set(src, resource);
return resource;
}
// 主应用组件
function App() {
return (
// 使用Suspense包裹异步组件,提供加载中的提示
<Suspense fallback={<p>Loading image...</p>}>
<ImageLoader src="https://www.baidu.com/img/flexible/logo/pc/result.png" />
</Suspense>
);
}
// 导出App组件
export default App;
React 引入了对数据加载的 Suspense 支持,通过挂起未完成的异步数据加载,让界面更直观地处理异步状态。
src\apps\App7.jsx
// 导入 React 的 use 和 Suspense 组件
import { use, Suspense } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义异步函数用于获取用户数据
async function fetchUserData(userId) {
// 发送 GET 请求到本地服务器获取用户数据
const response = await fetch(`http://localhost:3000/user/${userId}`);
// 将响应转换为 JSON 格式并返回
return response.json();
}
// 定义 UserProfile 组件,接收 user 属性
function UserProfile({ user }) {
// 使用 use hook 处理异步数据
const userData = use(user);
// 返回包含用户信息的 JSX
return (
<div>
<h1>{userData.name}</h1>
<p>{userData.email}</p>
</div>
);
}
// 为 UserProfile 组件定义 PropTypes
UserProfile.propTypes = {
user: PropTypes.object.isRequired,
};
// 定义主应用组件
function App() {
// 调用 fetchUserData 获取用户数据
const user = fetchUserData(123);
// 返回包含在 Suspense 中的 UserProfile 组件
return (
<Suspense fallback={<p>Loading...</p>}>
<UserProfile user={user} />
</Suspense>
);
}
// 导出 App 组件
export default App;
use:挂起数据获取,直到 Promise 完成。Suspense:在数据加载时显示 Loading data...。server.cjs
const express = require("express");
const cors = require("cors");
const app = express();
app.use(cors());
app.get("/user/:userId", (req, res) => {
const userId = req.params.userId;
res.json({ userId, name: "mary", email: "mary@qq.com" });
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
错误不会自动处理,需要结合 ErrorBoundary 使用:
<ErrorBoundary fallback={<p>Error occurred!</p>}>
<Suspense fallback={<p>Loading...</p>}>
<YourComponent />
</Suspense>
</ErrorBoundary>
Suspense 是 React 提供的用于管理异步数据加载的机制。它通过挂起(suspending)组件的渲染,直到其依赖的资源(例如数据或动态导入的代码)加载完成,同时显示 fallback 指定的备用 UI。
核心流程
检测挂起状态:
Promise),React 检测到这些资源未就绪时,会触发挂起。Suspense 会中断组件的渲染,等待异步任务完成。触发挂起:
Promise)会抛出一个挂起状态,通知 React 当前任务尚未完成。显示备用 UI:
Suspense 组件的 fallback。异步任务完成:
Promise resolved),组件重新开始渲染,并用实际内容替换 fallback。Suspense 支持同时管理多个挂起的 Promise:
Promise 必须解析,才能结束 Suspense 的挂起状态。src\apps\App8.jsx
// 导入 React 的 use 和 Suspense 组件
import { use, Suspense } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义一个模拟异步数据获取的函数,接收延迟时间参数
function fetchData(ms) {
// 返回一个 Promise,在指定时间后解析为字符串
return new Promise((resolve) => {
setTimeout(() => {
resolve(ms.toString());
}, ms);
});
}
// 定义第一个异步组件,接收 data 属性
function AsyncComponent({ data }) {
// 使用 use hook 处理异步数据
const result = use(data);
// 返回包含结果的 JSX
return <div>AsyncComponent {result}</div>;
}
// 为 AsyncComponent 定义 PropTypes
AsyncComponent.propTypes = {
data: PropTypes.string.isRequired,
};
// 定义第二个异步组件,接收 data 属性
function AnotherAsyncComponent({ data }) {
// 使用 use hook 处理异步数据
const result = use(data);
// 记录计时器结束时间
console.timeEnd("App");
// 返回包含结果的 JSX
return <div>AnotherAsyncComponent {result}</div>;
}
// 为 AnotherAsyncComponent 定义 PropTypes
AnotherAsyncComponent.propTypes = {
data: PropTypes.string.isRequired,
};
// 定义主应用组件
function App() {
// 开始计时
console.time("App");
// 创建两个延迟不同的异步数据请求
const data1 = fetchData(1000);
const data2 = fetchData(5000);
// 返回包含在 Suspense 中的两个异步组件
return (
<Suspense fallback={<div>加载中...</div>}>
<AsyncComponent data={data1} />
<AnotherAsyncComponent data={data2} />
</Suspense>
);
}
// 导出 App 组件
export default App;
Suspense 组件可以嵌套使用,管理不同子树的挂起状态:
src\apps\App9.jsx
import { use, Suspense } from "react";
import PropTypes from "prop-types";
function fetchData(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(ms.toString());
}, ms);
});
}
function LoadingA() {
return <div>加载组件A中...</div>;
}
function LoadingB() {
return <div>加载组件B中...</div>;
}
function ComponentA({ data }) {
const result = use(data);
return <div>组件A加载完成: {result}</div>;
}
ComponentA.propTypes = {
data: PropTypes.string.isRequired,
};
function ComponentB({ data }) {
const result = use(data);
return <div>组件B加载完成: {result}</div>;
}
ComponentB.propTypes = {
data: PropTypes.string.isRequired,
};
function App() {
const data1 = fetchData(1000);
const data2 = fetchData(3000);
return (
<Suspense fallback={<LoadingA />}>
<ComponentA data={data1} />
<Suspense fallback={<LoadingB />}>
<ComponentB data={data2} />
</Suspense>
</Suspense>
);
}
export default App;
src\apps\App12.jsx
// 从 React 中导入必要的 hooks 和组件
import { use, Suspense, useState } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 创建一个返回 Promise 的工具函数,接收值和延迟时间作为参数
const createPromise = (value, delay) => {
return new Promise((resolve) => {
setTimeout(() => resolve(value), delay);
});
};
// 显示数据的组件,接收一个 promise 作为 props
function DisplayData({ promise }) {
// 使用 use hook 来处理 Promise
const data = use(promise);
return <div>当前数据: {data}</div>;
}
// 为 DisplayData 组件定义 PropTypes
DisplayData.propTypes = {
promise: PropTypes.string.isRequired,
};
// 主组件
function Use() {
// 使用 useState 初始化一个 promise 状态,初始值是一个延迟 1 秒的 Promise
const [promise, setPromise] = useState(() => createPromise("初始数据", 1000));
return (
<div>
<h2>数据更新演示</h2>
{/* 使用 Suspense 包裹异步组件,提供加载状态 */}
<Suspense fallback={<div>加载中...</div>}>
<DisplayData promise={promise} />
</Suspense>
{/* 点击按钮时创建新的 Promise 并更新状态 */}
<button
onClick={() => {
setPromise(createPromise("更新的数据", 1000));
}}
>
更新数据
</button>
</div>
);
}
// 导出主组件
export default Use;
src\apps\App13.jsx
// 从 React 中导入必要的 hooks 和组件
import { use, Suspense, useState } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 创建一个返回 Promise 的工具函数,接收列表数据作为参数
const createPromise = (list) => {
return new Promise((resolve) => {
setTimeout(() => resolve(list), 1000);
});
};
// 显示单个列表项的组件,接收一个 promise 作为 props
function ItemDisplay({ promise }) {
// 使用 use hook 处理 Promise
const item = use(promise);
return <li>{item}</li>;
}
// 为 ItemDisplay 组件定义 PropTypes
ItemDisplay.propTypes = {
promise: PropTypes.string.isRequired,
};
// 显示整个列表的组件,接收一个 promises 数组作为 props
function ListDisplay({ promises }) {
return (
<ul>
{promises.map((promise, index) => (
// 为每个列表项添加 Suspense 包装
<Suspense key={index} fallback={<li>加载中...</li>}>
<ItemDisplay promise={promise} />
</Suspense>
))}
</ul>
);
}
// 为 ListDisplay 组件定义 PropTypes
ListDisplay.propTypes = {
promises: PropTypes.arrayOf(PropTypes.string).isRequired,
};
// 主应用组件
function App() {
// 使用 useState 初始化 promises 数组,初始值包含一个 Promise
const [promises, setPromises] = useState(() => [createPromise("数据 1")]);
return (
<div>
<h2>列表数据演示</h2>
<ListDisplay promises={promises} />
{/* 点击按钮时创建新的 Promise 并添加到列表中 */}
<button
onClick={() => {
const newPromise = createPromise(`数据 ${promises.length + 1}`);
setPromises([...promises, newPromise]);
}}
>
添加数据
</button>
</div>
);
}
// 导出主组件
export default App;
src\apps\App14.jsx
// 从 React 中导入必要的 hooks 和组件
import { use, Suspense, useState } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 模拟获取分页数据的函数,接收页码作为参数
const fetchPageData = (page) => {
return new Promise((resolve) => {
setTimeout(() => {
// 创建一个包含5个数据项的数组
const pageData = Array.from(
{ length: 5 },
(_, i) => `第${page}页的数据项 ${i + 1}`
);
resolve(pageData);
}, 1000);
});
};
// 显示单页数据的组件,接收一个 promise 作为 props
function PageData({ promise }) {
// 使用 use hook 处理 Promise
const data = use(promise);
return (
<div>
{/* 遍历数据数组并渲染每一项 */}
{data.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
// 为 PageData 组件定义 PropTypes
PageData.propTypes = {
promise: PropTypes.string.isRequired,
};
// 主应用组件
function Use() {
// 使用 useState 初始化页面数据数组,初始值为第一页的数据
const [pages, setPages] = useState(() => [fetchPageData(1)]);
// 使用 useState 跟踪当前页码
const [currentPage, setCurrentPage] = useState(1);
return (
<div>
<h2>分页数据加载演示</h2>
{/* 遍历并渲染所有页面的数据 */}
{pages.map((pagePromise, index) => (
<Suspense key={index} fallback={<div>正在加载第{index + 1}页...</div>}>
<PageData promise={pagePromise} />
</Suspense>
))}
{/* 点击按钮加载下一页数据 */}
<button
onClick={() => {
const nextPage = currentPage + 1;
setCurrentPage(nextPage);
setPages([...pages, fetchPageData(nextPage)]);
}}
>
加载更多
</button>
</div>
);
}
// 导出主组件
export default Use;
src\apps\App15.jsx
// 从 React 中导入必要的 hooks 和组件
import { use, Suspense, useState } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义一个获取搜索数据的函数,接收关键字作为参数
const fetchSearchData = (keyword) => {
// 创建一个 AbortController 实例用于取消请求
const controller = new AbortController();
// 发起带有 abort 信号的 fetch 请求
const promise = fetch(`http://localhost:3000/search?keyword=${keyword}`, {
signal: controller.signal,
})
// 将响应转换为 JSON
.then((res) => res.json())
// 如果请求被取消,返回空数组
.catch(() => []);
// 将 controller 附加到 promise 上以便后续使用
promise.controller = controller;
return promise;
};
// 显示搜索结果的组件,接收一个 promise 作为 props
function SearchResults({ promise }) {
// 使用 use hook 处理 Promise
const results = use(promise);
return (
<ul>
{/* 遍历搜索结果并渲染每一项 */}
{results.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
// 为 SearchResults 组件定义 PropTypes
SearchResults.propTypes = {
promise: PropTypes.string.isRequired,
};
// 主应用组件
function Use() {
// 使用 useState 管理搜索 Promise 和关键字
const [searchPromise, setSearchPromise] = useState(null);
const [keyword, setKeyword] = useState("");
// 处理搜索的函数
const handleSearch = (value) => {
// 如果存在之前的搜索请求,则取消它
if (searchPromise?.controller) {
searchPromise.controller.abort();
}
// 更新关键字状态
setKeyword(value);
// 如果输入值不为空,则发起新的搜索请求
setSearchPromise(value.trim() ? fetchSearchData(value) : null);
};
return (
<div>
<h2>搜索建议</h2>
{/* 搜索输入框 */}
<input
type="text"
value={keyword}
onChange={(e) => handleSearch(e.target.value)}
placeholder="请输入关键字"
/>
{/* 当存在搜索请求时显示结果 */}
{searchPromise && (
<Suspense fallback={<div>正在搜索{keyword}...</div>}>
<SearchResults promise={searchPromise} />
</Suspense>
)}
</div>
);
}
// 导出主组件
export default Use;
const express = require("express");
const cors = require("cors");
const app = express();
app.use(cors());
app.get("/user/:userId", (req, res) => {
const userId = req.params.userId;
res.json({ userId, name: "mary", email: "mary@qq.com" });
});
app.get("/search", (req, res) => {
const keyword = req.query.keyword;
setTimeout(() => {
const results = Array.from({ length: 10 }, (_, i) => `${keyword}${i + 1}`);
res.json(results);
}, 1000);
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
useRef 是 React 提供的一个 Hook,用于管理不参与组件渲染流程的可变引用(ref)。它非常轻量级且强大,适合在某些场景下替代 state,尤其是当数据的变化不需要触发组件重新渲染时。
useImperativeHandle 是 React 提供的一个高级 Hook,用于自定义通过 ref 暴露给父组件的实例值或方法。让父组件可以通过 ref 访问子组件中自定义的值或方法,而不仅仅是 DOM 节点。
src\apps\App16.jsx
// 导入 React 的 useRef 和 useImperativeHandle hooks
import { useRef, useImperativeHandle } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义子组件 ChildInput,接收 ref 参数
function ChildInput({ ref }) {
// 创建一个 ref 用于引用 input 元素
const inputRef = useRef();
// 使用 useImperativeHandle 自定义暴露给父组件的方法
useImperativeHandle(
ref,
() => {
return {
// 聚焦方法
focus: () => {
inputRef.current.focus();
},
// 获取输入值方法
getValue: () => {
return inputRef.current.value;
},
// 设置输入值方法
setValue: (value) => {
inputRef.current.value = value;
},
};
},
[]
);
// 渲染带 ref 的 input 元素
return <input ref={inputRef} />;
}
// 为 ChildInput 组件添加 PropTypes 类型检查
ChildInput.propTypes = {
ref: PropTypes.object,
};
// 定义父组件 Use
function Use() {
// 创建一个 ref 用于引用子组件
const childRef = useRef();
// 点击按钮时的处理函数
const handleClick = () => {
// 调用子组件的 focus 方法
childRef.current.focus();
// 调用子组件的 setValue 方法设置值
childRef.current.setValue("你好 React 19!");
// 调用子组件的 getValue 方法并打印值
console.log("当前输入值:", childRef.current.getValue());
};
// 渲染组件
return (
<div>
<h3>Ref 传递示例</h3>
<ChildInput ref={childRef} />
<button onClick={handleClick}>设置输入框</button>
</div>
);
}
// 导出 Use 组件
export default Use;
src\apps\App17.jsx
// 导入 React 的 useRef 和 useImperativeHandle hooks
import { useRef, useImperativeHandle } from "react";
// 导入 PropTypes 用于类型检查
import PropTypes from "prop-types";
// 定义 Tooltip 组件,接收 ref 参数
const Tooltip = ({ ref }) => {
// 创建一个 ref 用于引用提示框元素
const tooltipRef = useRef();
// 使用 useImperativeHandle 自定义暴露给父组件的方法
useImperativeHandle(ref, () => ({
// 显示提示框的方法
show: (event) => {
// 获取鼠标点击位置的坐标
const x = event.clientX;
const y = event.clientY;
// 设置提示框显示并定位
tooltipRef.current.style.display = "block";
tooltipRef.current.style.left = `${x + 10}px`;
tooltipRef.current.style.top = `${y + 10}px`;
// 设置初始透明度为0
tooltipRef.current.style.opacity = "0";
// 触发重排以确保过渡动画生效
tooltipRef.current.offsetHeight;
// 设置透明度为1,触发淡入动画
tooltipRef.current.style.opacity = "1";
},
// 隐藏提示框的方法
hide: () => {
// 设置透明度为0,触发淡出动画
tooltipRef.current.style.opacity = "0";
// 2秒后隐藏提示框
setTimeout(() => {
tooltipRef.current.style.display = "none";
}, 2000);
},
}));
// 渲染提示框元素
return (
<div
ref={tooltipRef}
style={{
display: "none",
position: "fixed",
backgroundColor: "#333",
color: "white",
padding: "8px 12px",
borderRadius: "4px",
fontSize: "14px",
transition: "opacity 2s ease",
zIndex: 1000,
}}
>
这是一个提示框
</div>
);
};
// 为 Tooltip 组件添加 PropTypes 类型检查
Tooltip.propTypes = {
ref: PropTypes.object,
};
// 定义主应用组件
function App() {
// 创建一个 ref 用于引用 Tooltip 组件
const tooltipRef = useRef();
// 鼠标进入时的处理函数
const handleMouseEnter = (e) => {
tooltipRef.current.show(e);
};
// 鼠标离开时的处理函数
const handleMouseLeave = () => {
tooltipRef.current.hide();
};
// 渲染主应用组件
return (
<div>
<button onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
鼠标悬停显示提示
</button>
<Tooltip ref={tooltipRef} />
</div>
);
}
// 导出主应用组件
export default App;
src\apps\App18.jsx
// 导入必要的React钩子
import { createContext, use, useState } from "react";
// 创建语言上下文,默认值为'zh'(中文)
const LangContext = createContext("zh");
// 定义多语言文本对象
const messages = {
// 中文文本
zh: {
greeting: "你好,世界!",
switchLang: "切换语言",
},
// 英文文本
en: {
greeting: "Hello World!",
switchLang: "Switch Language",
},
};
// 定义语言文本显示组件
function LanguageText() {
// 使用上下文获取当前语言
const lang = use(LangContext);
return (
<div>
<h2>{messages[lang].greeting}</h2>
</div>
);
}
// 定义主应用组件
function App() {
// 定义语言状态,默认为中文
const [lang, setLang] = useState("zh");
return (
// 使用LangContext提供语言上下文
<LangContext value={lang}>
<div>
{/* 渲染语言文本组件 */}
<LanguageText />
{/* 切换语言按钮 */}
<button onClick={() => setLang(lang === "zh" ? "en" : "zh")}>
{messages[lang].switchLang}
</button>
</div>
</LangContext>
);
}
// 导出主应用组件
export default App;
useDeferredValue 是 React 引入的一个新 Hook,用于实现延迟更新。它可以帮助在 React 应用中优化性能,尤其是在处理高优先级和低优先级更新时提供更流畅的用户体验。
useDeferredValue 接受一个值并返回一个延迟的值(deferred value)。如果传入的值在一段时间内发生了变化,useDeferredValue 会延迟更新返回的值,优先让其他更高优先级的任务完成。
// 导入React的useState和useDeferredValue钩子
import { useState, useDeferredValue } from "react";
// 导入PropTypes用于类型检查
import PropTypes from "prop-types";
// 定义主应用组件
function App() {
// 定义搜索查询状态
const [query, setQuery] = useState("");
// 使用useDeferredValue创建延迟更新的查询值
const deferredQuery = useDeferredValue(query);
// 使用延迟查询值执行昂贵的搜索操作
const searchResults = expensiveSearchFunction(deferredQuery);
return (
<div>
{/* 搜索输入框 */}
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
{/* 渲染搜索结果组件 */}
<SearchResults results={searchResults} />
</div>
);
}
// 模拟耗时的搜索函数
function expensiveSearchFunction(query) {
console.log("Computing search results for:", query);
// 创建20000个模拟搜索结果
return Array(20000)
.fill(0)
.map((_, idx) => `Result ${idx} for "${query}"`);
}
// 定义SearchResults组件的属性类型
SearchResults.propTypes = {
results: PropTypes.array.isRequired,
};
// 定义搜索结果显示组件
function SearchResults({ results }) {
return (
<ul>
{/* 遍历并渲染所有搜索结果 */}
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
);
}
// 导出主应用组件
export default App;
useTransition 是 React 引入的一个新 Hook,用于控制更新优先级,允许将某些更新标记为“过渡任务”(Transition)。通过将任务分为高优先级和低优先级,可以提升用户体验,尤其是在处理复杂、耗时的更新时。
// 导入React的useState和useTransition钩子
import { useState, useTransition } from "react";
// 导入PropTypes用于类型检查
import PropTypes from "prop-types";
// 定义主应用组件
function App() {
// 定义搜索查询状态
const [query, setQuery] = useState("");
// 定义搜索结果状态
const [results, setResults] = useState([]);
// 使用useTransition创建过渡状态和启动函数
const [isPending, startTransition] = useTransition();
// 处理输入变化的函数
const handleChange = (e) => {
// 获取输入值
const value = e.target.value;
// 立即更新查询状态(高优先级)
setQuery(value);
// 使用startTransition包装低优先级更新
startTransition(() => {
// 执行昂贵的搜索操作
const searchResults = expensiveSearchFunction(value);
// 更新搜索结果
setResults(searchResults);
});
};
// 渲染UI
return (
<div>
{/* 搜索输入框 */}
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search..."
/>
{/* 渲染搜索结果组件 */}
<SearchResults results={results} isPending={isPending} />
</div>
);
}
// 定义SearchResults组件的属性类型
SearchResults.propTypes = {
results: PropTypes.array.isRequired,
isPending: PropTypes.bool.isRequired,
};
// 模拟耗时的搜索函数
function expensiveSearchFunction(query) {
console.log("Computing search results for:", query);
// 创建10000个模拟搜索结果
return Array(10000)
.fill(0)
.map((_, idx) => `Result ${idx} for "${query}"`);
}
// 定义搜索结果显示组件
function SearchResults({ results, isPending }) {
// 返回搜索结果列表,当isPending为true时降低透明度
return (
<ul style={{ opacity: isPending ? 0.5 : 1 }}>
{/* 遍历并渲染所有搜索结果 */}
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
);
}
// 导出主应用组件
export default App;
FormAction 的 action 属性类似于传统 HTML 表单的 action,但它接受一个函数而不是 URL。
自动处理 FormData
FormData 对象preventDefault()简化的数据访问
const formAction = (formData) => {
const name = formData.get("name"); // 直接通过 name 属性获取值
const email = formData.get("email");
};
src\apps\App21.jsx
// 导入 React 的 useState 钩子
import { useState } from "react";
// 定义主应用组件
function App() {
// 定义状态变量 message 和更新函数 setMessage
const [message, setMessage] = useState("");
// 定义表单提交处理函数
const formAction = (formData) => {
// 从表单数据中获取姓名
const name = formData.get("name");
// 从表单数据中获取邮箱
const email = formData.get("email");
// 设置成功提交消息
setMessage(`表单提交成功! 姓名: ${name}, 邮箱: ${email}`);
};
// 返回组件JSX结构
return (
// 最外层容器
<div>
{/* 表单组件,设置提交处理函数 */}
<form action={formAction}>
{/* 姓名输入区域容器 */}
<div>
{/* 姓名输入标签 */}
<label htmlFor="name">姓名:</label>
{/* 姓名输入框 */}
<input type="text" id="name" name="name" required />
</div>
{/* 邮箱输入区域容器 */}
<div>
{/* 邮箱输入标签 */}
<label htmlFor="email">邮箱:</label>
{/* 邮箱输入框 */}
<input type="email" id="email" name="email" required />
</div>
{/* 提交按钮 */}
<button type="submit">提交</button>
{/* 第二个提交按钮,使用相同的处理函数 */}
<button type="submit" formAction={formAction}>
提交2
</button>
</form>
{/* 显示提交后的消息 */}
<div>{message}</div>
</div>
);
}
// 导出App组件
export default App;
useFormStatus 是 React 提供的一个钩子,用于获取表单的提交状态。它返回一个对象,其中包含 pending 属性,表示表单是否正在提交。
// 导入 React 的 useState 钩子
import { useState } from "react";
// 导入 useFormStatus 钩子用于表单状态管理
import { useFormStatus } from "react-dom";
// 定义提交按钮组件
function SubmitButton() {
// 获取表单提交状态
const { pending } = useFormStatus();
// 返回按钮组件,根据pending状态显示不同文本和禁用状态
return (
<button type="submit" disabled={pending}>
{pending ? "提交中..." : "提交"}
</button>
);
}
// 定义主应用组件
function App() {
// 定义消息状态
const [message, setMessage] = useState("");
// 获取表单提交状态
const { pending } = useFormStatus();
// 定义异步表单提交处理函数
async function formAction(formData) {
// 从表单数据中获取姓名和邮箱
const name = formData.get("name");
const email = formData.get("email");
try {
// 模拟异步操作,延迟2秒
await new Promise((resolve) => setTimeout(resolve, 2000));
// 设置成功提交消息
setMessage(`表单数据: 姓名: ${name}, 邮箱: ${email}`);
} catch {
// 设置失败提示消息
setMessage("提交失败,请重试");
}
}
// 返回组件JSX结构
return (
<div>
{/* 表单组件,设置提交处理函数 */}
<form action={formAction}>
{/* 姓名输入区域 */}
<div>
<label htmlFor="name">姓名:</label>
<input
type="text"
id="name"
name="name"
required
disabled={pending}
/>
</div>
{/* 邮箱输入区域 */}
<div>
<label htmlFor="email">邮箱:</label>
<input
type="email"
id="email"
name="email"
required
disabled={pending}
/>
</div>
{/* 提交按钮组件 */}
<SubmitButton />
</form>
{/* 显示提交消息 */}
<div>{message}</div>
</div>
);
}
// 导出App组件
export default App;
useActionState 是 React 提供的一个钩子,用于在表单提交后处理状态。它返回一个对象,其中包含 data 和 error 属性,分别表示表单提交后的数据和错误信息。
// 导入 React 的 useActionState Hook
import { useActionState } from "react";
// 定义 App 组件
function App() {
// 使用 useActionState 处理表单状态和提交
// state: 表单状态对象
// formAction: 表单提交处理函数
// pending: 是否正在提交
const [state, formAction, pending] = useActionState(
// 异步处理表单提交的函数
async (currentState, formData) => {
// 打印上一次的状态
console.log("上一次状态:", currentState);
// 获取表单数据
const name = formData.get("name");
const password = formData.get("password");
// 验证密码长度
if (!password || password.length < 6) {
return {
error: { message: "密码长度不能小于6位" },
data: currentState.data,
};
}
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 1000));
// 返回成功状态和数据
return {
error: null,
data: {
message: "注册成功",
name,
password,
previousName: currentState.data.name,
},
};
},
// 初始状态
{ data: { name: "", previousName: "" }, error: null }
);
// 返回表单 JSX
return (
// 表单组件,绑定提交处理函数
<form action={formAction}>
{/* 用户名输入区域 */}
<div>
<label>用户名:</label>
<input name="name" disabled={pending} />
</div>
{/* 密码输入区域 */}
<div>
<label>密码:</label>
<input name="password" type="password" disabled={pending} />
</div>
{/* 提交按钮 */}
<button type="submit" disabled={pending}>
{pending ? "注册中..." : "注册"}
</button>
{/* 错误信息显示 */}
{state.error && <div style={{ color: "red" }}>{state.error.message}</div>}
{/* 成功信息显示 */}
{state.data && <div style={{ color: "green" }}>{state.data.message}</div>}
</form>
);
}
// 导出 App 组件
export default App;
useOptimistic 是 React 19 中新增的一个 Hook,专门设计用于处理乐观更新的场景。乐观更新是一种优化技术,用于在用户与界面交互时,立即更新 UI,从而提供更流畅的体验,而无需等待后端的响应。
useOptimistic 提供了一种声明式的方法来管理乐观状态。它的基本形式如下:
const [optimisticState, addOptimisticUpdate] = useOptimistic(
initialState,
reducer
);
// 导入必要的 React Hooks
import { useState, useOptimistic, useTransition } from "react";
// 定义计数器组件
function Counter() {
// 使用 useState 管理计数状态
const [count, setCount] = useState(0);
// 使用 useTransition 处理异步状态更新
const [isPending, startTransition] = useTransition();
// 使用 useOptimistic 实现乐观更新
// state 是当前状态,action 包含更新类型
const [optimisticCount, addOptimisticUpdate] = useOptimistic(
count,
(state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
}
);
// 处理增加按钮点击事件
const handleIncrement = async () => {
// 使用 startTransition 包装异步操作
startTransition(async () => {
// 立即进行乐观更新
addOptimisticUpdate({ type: "increment" });
try {
// 模拟异步请求,1秒后失败
await new Promise((resolve, reject) => setTimeout(reject, 1000));
// 更新实际计数
setCount((count) => count + 1);
} catch (error) {
// 请求失败时输出错误并回滚状态
console.error("服务器请求失败,可能需要回滚", error);
}
});
};
// 渲染组件UI
return (
<div>
{/* 显示当前计数值 */}
<p>Count: {optimisticCount}</p>
{/* 根据加载状态显示不同内容 */}
{isPending ? (
<p>Loading...</p>
) : (
<button onClick={handleIncrement}>+</button>
)}
</div>
);
}
// 导出计数器组件
export default Counter;
// 导入必要的 React Hooks
import { useState, useOptimistic, useTransition } from "react";
// 定义主应用组件
function App() {
// 使用 useState 定义待办事项列表状态
const [todos, setTodos] = useState([
{ id: crypto.randomUUID(), text: "学习React" },
{ id: crypto.randomUUID(), text: "学习useOptimistic" },
]);
// 使用 useTransition 处理异步状态更新
const [isPending, startTransition] = useTransition();
// 使用 useOptimistic 实现乐观更新
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, action) => {
switch (action.type) {
case "add":
return [...state, action.todo];
case "delete":
return state.filter((todo) => todo.id !== action.id);
default:
return state;
}
}
);
// 处理添加待办事项的函数
const handleAdd = async (formData) => {
const text = formData.get("todo").trim();
if (!text) return;
// 生成临时ID
const optimisticId = crypto.randomUUID();
// 创建临时待办事项对象
const optimisticTodo = {
id: optimisticId,
text,
};
// 使用 startTransition 包装异步操作
startTransition(async () => {
// 立即进行乐观更新
addOptimisticTodo({ type: "add", todo: optimisticTodo });
try {
// 模拟服务器请求,生成新ID
const serverGeneratedId = await new Promise((resolve) =>
setTimeout(() => resolve(crypto.randomUUID()), 1000)
);
// 创建实际待办事项对象
const actualTodo = {
id: serverGeneratedId,
text,
};
// 更新实际状态
setTodos((prev) => [...prev, actualTodo]);
} catch (error) {
console.error("添加失败:", error);
}
});
};
// 处理删除待办事项的函数
const handleDelete = async (id) => {
startTransition(async () => {
// 立即进行乐观更新
addOptimisticTodo({ type: "delete", id });
try {
// 模拟服务器请求
await new Promise((resolve) => setTimeout(resolve, 1000));
// 更新实际状态
setTodos((prev) => prev.filter((todo) => todo.id !== id));
} catch (error) {
console.error("删除失败:", error);
}
});
};
// 渲染组件UI
return (
<div>
<h1>待办事项</h1>
{/* 空注释用于保持格式 */}
{/* 添加待办事项的表单 */}
<form>
<input
type="text"
name="todo"
placeholder="输入新的待办事项"
disabled={isPending}
/>
<button formAction={handleAdd} disabled={isPending}>
{isPending ? "添加中..." : "添加"}
</button>
</form>
<ul>
{optimisticTodos.map((todo) => (
<li key={todo.id}>
{todo.text}
<button onClick={() => handleDelete(todo.id)} disabled={isPending}>
删除
</button>
</li>
))}
</ul>
</div>
);
}
// 导出 App 组件
export default App;
当调用 startTransition 时
handleAdd 工作流程
React Compiler 是 React 团队推出的一个新编译器,旨在构建阶段自动优化 React 应用程序。它能够理解 React 的规则,并在不需要重写代码的情况下,对组件和钩子中的值或值组进行自动记忆化处理,从而减少不必要的重新计算,提升应用性能。
主要功能:
自动记忆化处理:类似于手动使用 useMemo、useCallback 和 React.memo,但由编译器自动完成,降低手动记忆化的复杂性和可能的错误。
ESLint 插件:提供 ESLint 插件,在编辑器中直接显示编译器的分析结果,帮助开发者遵循 React 规则,提升代码质量。
React Compiler 当前处于 Beta 阶段,可在 React 17 及以上版本的应用程序和库中使用。安装方式如下:
npm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
或使用 Yarn:
yarn add -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
React Compiler Healthcheck 是一个工具,用于帮助开发者分析代码库是否符合 React Compiler(如 React Forget 编译器)的优化要求,以及检测可能影响自动化优化的代码问题。它的目标是提升代码质量并确保编译器可以正确处理代码,最大化 React 应用的性能潜力。
npx react-compiler-healthcheck
Successfully compiled 54 out of 55 components.
StrictMode usage found.
Found no usage of incompatible libraries.
eslint-plugin-react-compiler 是 React Compiler 的 ESLint 插件,用于在代码审查和开发过程中检测和修复不符合 React Compiler 规则的问题。
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
+import reactCompiler from 'eslint-plugin-react-compiler'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
+ 'react-compiler': reactCompiler,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
+ 'react-compiler/react-compiler': 'error',
},
},
]
babel-plugin-react-compiler 是 React Compiler 的 Babel 插件,用于在构建过程中自动优化 React 应用程序。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+const ReactCompilerConfig = {};
export default defineConfig({
plugins: [react({
+ babel: {
+ plugins: [
+ ["babel-plugin-react-compiler", ReactCompilerConfig],
+ ],
+ },
})],
})
/* function App() {
return <div>Hello World</div>;
} */
// 方式1:利用组件实例
function _c(size) {
// 获取当前组件实例
const instance = React.getCurrentInstance();
// 如果实例上没有缓存,则创建新的缓存
if (!instance._cache) {
instance._cache = new Array(size);
instance._cache.fill(Symbol.for("react.memo_cache_sentinel"));
}
return instance._cache;
}
// 方式2:利用闭包
function Component() {
// 缓存数组只在组件首次渲染时创建
const cache = React.useMemo(() => {
const arr = new Array(1);
arr.fill(Symbol.for("react.memo_cache_sentinel"));
return arr;
}, []);
// 后续渲染复用同一个cache
const $ = cache;
}
function App() {
// 使用 React Compiler 初始化缓存,分配一个大小为 1 的缓存数组
const cache = _c(1);
// 定义用于存储 JSX 元素的变量
let jsxElement;
// 检查缓存的第一个位置是否是哨兵值,表示未缓存
if (cache[0] === Symbol.for("react.memo_cache_sentinel")) {
// 如果是哨兵值,创建新的 JSX 元素
jsxElement = <div>Hello World</div>;
// 将生成的 JSX 元素存入缓存
cache[0] = jsxElement;
} else {
// 如果缓存中已有值,直接复用缓存的 JSX 元素
jsxElement = cache[0];
}
// 返回 JSX 元素,复用或重新生成的结果
return jsxElement;
}
export default App;
import { useState } from "react";
/* function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<button onClick={handleClick}>{count}</button>
);
} */
function Counter() {
// 初始化缓存数组,支持最多 5 个缓存槽位
const cache = _c(5);
// 使用 useState 创建状态和更新函数
const [count, setCount] = useState(0);
// 缓存点击处理函数
let incrementHandler;
if (cache[0] !== count) {
// 如果缓存中的计数值与当前计数不一致,更新缓存
incrementHandler = () => setCount(count + 1);
cache[0] = count; // 缓存当前计数值
cache[1] = incrementHandler; // 缓存生成的点击处理函数
} else {
// 如果缓存一致,直接复用处理函数
incrementHandler = cache[1];
}
// 缓存按钮 JSX 元素
let buttonElement;
if (cache[2] !== count || cache[3] !== incrementHandler) {
// 如果计数值或处理函数发生变化,更新缓存
buttonElement = <button onClick={incrementHandler}>{count}</button>;
cache[2] = count; // 缓存当前计数值
cache[3] = incrementHandler; // 缓存当前点击处理函数
cache[4] = buttonElement; // 缓存生成的按钮元素
} else {
// 如果缓存一致,直接复用按钮元素
buttonElement = cache[4];
}
// 返回按钮元素
return buttonElement;
}
export default Counter;
import { useState } from "react";
//import PropTypes from 'prop-types';
/* function Child({ count, onIncrement }) {
return (
<button onClick={onIncrement}>
子组件中的计数: {count}
</button>
);
}
Child.propTypes = {
count: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired
};
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div>
<h2>父组件</h2>
<Child
count={count}
onIncrement={handleIncrement}
/>
</div>
);
} */
function Child(props) {
const cache = _c(3); // 缓存初始化,用于存储优化结果
const { count, onIncrement } = props; // 解构 props
let buttonElement;
// 检查缓存是否需要更新
if (cache[0] !== count || cache[1] !== onIncrement) {
// 如果依赖变化,重新创建 JSX 元素
buttonElement = (
<button onClick={onIncrement}>子组件中的计数: {count}</button>
);
cache[0] = count; // 更新缓存的依赖
cache[1] = onIncrement;
cache[2] = buttonElement; // 更新缓存的 JSX 元素
} else {
// 如果依赖未变,复用缓存的 JSX 元素
buttonElement = cache[2];
}
return buttonElement; // 返回按钮元素
}
function Counter() {
const cache = _c(6); // 缓存初始化
const [count, setCount] = useState(0); // 状态定义
// 缓存 handleIncrement 函数
let handleIncrement;
if (cache[0] !== count) {
handleIncrement = () => setCount(count + 1);
cache[0] = count;
cache[1] = handleIncrement;
} else {
handleIncrement = cache[1];
}
// 缓存标题元素
let titleElement;
if (cache[2] === Symbol.for("react.memo_cache_sentinel")) {
titleElement = <h2>父组件</h2>;
cache[2] = titleElement;
} else {
titleElement = cache[2];
}
// 缓存整个组件的 JSX
let containerElement;
if (cache[3] !== count || cache[4] !== handleIncrement) {
containerElement = (
<div>
{titleElement}
<Child count={count} onIncrement={handleIncrement} />
</div>
);
cache[3] = count;
cache[4] = handleIncrement;
cache[5] = containerElement;
} else {
containerElement = cache[5];
}
return containerElement; // 返回最终的组件结构
}
export default Counter;
useRef 是 React 提供的一个 Hook,用于管理不参与组件渲染流程的可变引用(ref)。它非常轻量级且强大,适合在某些场景下替代 state,尤其是当数据的变化不需要触发组件重新渲染时。
基本语法
const refContainer = useRef(initialValue);
initialValue: 引用的初始值,可以是任意类型(如 null、数字、对象、DOM 节点等)。refContainer: 返回一个对象,结构为 { current: initialValue },current 属性可以读写。常见使用场景
访问 DOM 元素useRef 常用于直接访问 DOM 元素,替代 document.querySelector 或 getElementById。
保存不随渲染改变的变量
用于存储跨渲染周期的可变值,而不触发重新渲染。
保存定时器或外部资源的引用
在管理 setInterval 或某些外部 API 时,useRef 是一个很好的选择。
避免闭包问题
在函数中避免访问陈旧的变量值。
详细示例
1. 访问 DOM 元素
通过 useRef 获取并操作某个 DOM 节点:
import React, { useRef } from "react";
function FocusInput() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus(); // 直接操作 DOM
};
return (
<div>
<input
ref={inputRef}
type="text"
placeholder="Click the button to focus me"
/>
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default FocusInput;
ref 绑定到 <input>,useRef 创建的引用 inputRef 可以访问对应的 DOM 节点。inputRef.current.focus(),可以控制输入框的焦点。2. 保存不随渲染改变的变量
某些值需要在组件生命周期中保持不变,但又不适合放在 state 中,因为它们的变化不需要触发重新渲染。
import React, { useRef, useState } from "react";
function Counter() {
const renderCount = useRef(0);
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
renderCount.current += 1;
return (
<div>
<p>Count: {count}</p>
<p>Rendered {renderCount.current} times</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default Counter;
useImperativeHandle 是 React 提供的一个高级 Hook,用于自定义通过 ref 暴露给父组件的实例值或方法。让父组件可以通过 ref 访问子组件中自定义的值或方法,而不仅仅是 DOM 节点。
基本语法
useImperativeHandle(ref, createHandle, [dependencies]);
ref: 传递自父组件的 refcreateHandle: 一个返回对象的函数,定义要暴露给父组件的值或方法。dependencies: 可选的依赖数组,当依赖发生变化时,createHandle 会重新执行。使用场景
自定义暴露的方法
ref 控制焦点、重置表单等。隐藏内部实现细节
详细示例
1. 控制 DOM 元素的行为
通过 useImperativeHandle 暴露方法,让父组件可以控制子组件的 DOM 行为。
import React, { useRef, useImperativeHandle } from "react";
const CustomInput = forwardRef(({ ref }) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = "";
},
}));
return <input ref={inputRef} type="text" placeholder="Custom Input" />;
});
function App() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
<button onClick={() => inputRef.current.clear()}>Clear Input</button>
</div>
);
}
export default App;
forwardRef:允许 CustomInput 接收 ref 参数。useImperativeHandle:暴露了 focus 和 clear 两个方法供父组件调用。2. 自定义复杂组件行为
为复杂的子组件暴露特定方法,例如表单的验证和重置。
import React, { forwardRef, useImperativeHandle, useState } from "react";
const Form = forwardRef((props, ref) => {
const [formData, setFormData] = useState({ name: "", email: "" });
useImperativeHandle(ref, () => ({
reset: () => {
setFormData({ name: "", email: "" });
},
validate: () => {
return formData.name !== "" && formData.email !== "";
},
}));
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
return (
<div>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
</div>
);
});
function App() {
const formRef = useRef();
const handleReset = () => {
formRef.current.reset();
};
const handleValidate = () => {
const isValid = formRef.current.validate();
alert(isValid ? "Form is valid!" : "Form is invalid!");
};
return (
<div>
<Form ref={formRef} />
<button onClick={handleReset}>Reset Form</button>
<button onClick={handleValidate}>Validate Form</button>
</div>
);
}
export default App;
reset:父组件可以调用这个方法重置表单数据。validate:父组件可以验证表单状态。工作原理
Ref:
ref,为 useImperativeHandle 提供基础。暴露对象:
useImperativeHandle 返回一个对象,定义子组件希望暴露的功能或值。隔离内部实现:
useImperativeHandle 中定义的内容,无法直接修改子组件的内部状态或操作未暴露的逻辑。注意事项
搭配 forwardRef 使用:
useImperativeHandle 必须配合 forwardRef,否则父组件无法传递 ref。依赖数组的使用:
dependencies 数组中,确保更新后的值正确暴露。谨慎使用:
useImperativeHandle 是一种相对低级的 API,应在必要时才使用,例如控制外部无法直接访问的行为或细节。props 进行父子组件间的通信。优点与限制
优点:
限制:
总结
useImperativeHandle 是 React 中一个强大的工具,用于自定义组件暴露给父组件的功能。
它适用于需要通过 ref 控制子组件行为的场景,比如自定义 DOM 操作、管理复杂表单、或暴露方法给父组件调用。
如果需要直接操控组件行为,并且这些行为不适合通过 props 管理,那么 useImperativeHandle 是一个非常适合的工具。
在 React 中,createContext是创建上下文的核心 API,它允许开发者在组件树中传递数据,而无需通过每个层级显式传递 props。这个功能非常适合管理全局状态或共享数据,如主题、用户信息等。
**** 创建上下文
使用createContext可以创建一个上下文对象,该对象代表了组件可以读取或提供的上下文。创建上下文的基本语法如下:
import { createContext } from "react";
const MyContext = createContext(defaultValue);
null或任何合适的初始值。**** 使用上下文
一旦创建了上下文对象,您可以使用它来包裹组件并提供上下文值。
<MyContext value={/* some value */}>
{/* Your components */}
</MyContext>
在这个例子中,所有被包裹的组件都可以访问到提供的值。
**** 读取上下文
要在组件中读取上下文,可以使用useContext钩子,或者在类组件中使用contextType。以下是函数组件中使用useContext的示例:
import { useContext } from "react";
function MyComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
对于类组件,可以这样使用:
class MyComponent extends React.Component {
static contextType = MyContext;
render() {
return <div>{this.context}</div>;
}
}
React 19 的新特性
在 React 19 中,除了传统的使用方式外,引入了新的use()函数,使得在条件和循环中访问上下文变得更加灵活。例如:
import { use } from "react";
function FeatureComponent({ featureName }) {
let featureEnabled = false;
if (featureName) {
featureEnabled = use(FeatureContext);
return featureEnabled ? (
<div>Feature Enabled!</div>
) : (
<div>Feature Disabled.</div>
);
}
}
这个新特性允许开发者在需要时按需获取上下文值,从而优化性能,减少不必要的重新渲染。
总结
createContext是 React 中强大的工具,使得跨层级共享数据变得简单。在 React 19 中,通过引入新的 API 和钩子,开发者可以更灵活地管理和访问上下文,提高了代码的可读性和性能。通过合理使用这些功能,可以大大简化状态管理和数据共享的复杂性。
useDeferredValue 是 React 18 引入的一个新 Hook,用于实现延迟更新。它可以帮助在 React 应用中优化性能,尤其是在处理高优先级和低优先级更新时提供更流畅的用户体验。
基本用法
useDeferredValue 接受一个值并返回一个延迟的值(deferred value)。如果传入的值在一段时间内发生了变化,useDeferredValue 会延迟更新返回的值,优先让其他更高优先级的任务完成。
语法:
const deferredValue = useDeferredValue(value);
value: 原始值,通常是快速变化的状态值。deferredValue: 一个与 value 同步但可能会延迟的值。适用场景
主要用于在用户输入或其他高优先级任务与复杂渲染之间平衡性能,例如:
核心特点
useDeferredValue 的更新优先级。示例代码
输入搜索场景
import React, { useState, useDeferredValue } from "react";
function App() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const searchResults = expensiveSearchFunction(deferredQuery);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<SearchResults results={searchResults} />
</div>
);
}
function expensiveSearchFunction(query) {
// 模拟一个繁重的计算逻辑
console.log("Computing search results for:", query);
return Array(10000)
.fill(0)
.map((_, idx) => `Result ${idx} for "${query}"`);
}
function SearchResults({ results }) {
return (
<ul>
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
);
}
export default App;
运行流程:
query 会立即更新,用于确保输入框响应流畅。useDeferredValue 返回的 deferredQuery 可能会稍有延迟,确保繁重的搜索计算不会阻塞用户输入。与 useTransition 的对比
useDeferredValue 是一个低级 Hook,专注于延迟一个值。useTransition 是一个高级 Hook,允许你手动标记更新为“过渡任务”,更灵活但需要配合事件触发。示例:
const [isPending, startTransition] = useTransition();
function handleInputChange(e) {
const value = e.target.value;
startTransition(() => {
setSearchQuery(value);
});
}
useTransition 更适用于全局更新调度,而 useDeferredValue 是针对特定值的优化。
注意事项
deferredValue 是一个状态值,类似于 useMemo,需要在依赖更新时重新计算。总结:useDeferredValue 是一个强大的性能优化工具,用于高频更新场景,但需要结合 React 的并发模式才能最大化发挥其优势。在复杂的交互和计算任务中,它能显著提升用户体验。
useTransition 是 React 引入的一个新 Hook,用于控制更新优先级,允许将某些更新标记为“过渡任务”(Transition)。通过将任务分为高优先级和低优先级,可以提升用户体验,尤其是在处理复杂、耗时的更新时。
基本用法
useTransition 的核心在于异步调度,确保高优先级任务(如用户输入)始终快速响应,而将低优先级任务(如复杂渲染)延迟执行。
语法:
const [isPending, startTransition] = useTransition();
isPending: 一个布尔值,表示当前是否有处于挂起状态的过渡任务。startTransition: 一个函数,用于定义需要延迟执行的更新逻辑。核心概念
高优先级 vs 低优先级任务
挂起状态
isPending 为 true,可以用它来显示加载指示或其他 UI 提示。示例代码
输入搜索场景
import React, { useState, useTransition } from "react";
function App() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // 高优先级任务:立即更新输入框
startTransition(() => {
const searchResults = expensiveSearchFunction(value);
setResults(searchResults); // 低优先级任务:延迟更新结果
});
};
return (
<div>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search..."
/>
{isPending && <p>Loading...</p>}
<SearchResults results={results} />
</div>
);
}
function expensiveSearchFunction(query) {
// 模拟一个繁重的计算逻辑
console.log("Computing search results for:", query);
return Array(10000)
.fill(0)
.map((_, idx) => `Result ${idx} for "${query}"`);
}
function SearchResults({ results }) {
return (
<ul>
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
);
}
export default App;
运行流程:
query 会立即更新(高优先级任务)。startTransition 延迟,防止卡顿。isPending 为 true,显示“Loading...”提示。useTransition 的优点
与 useDeferredValue 的对比
useTransition: 用于延迟触发某些状态更新,需要显式调用 startTransition。useDeferredValue: 用于延迟某个状态值的变化,自动调节优先级。对比场景:
useTransition。useDeferredValue。注意事项
useTransition 的延迟机制依赖 React 的 Concurrent Mode。总结
useTransition 是 React 提供的性能优化工具,适用于需要将更新分为高低优先级的复杂场景。它通过标记低优先级任务为“过渡任务”,为用户提供更加流畅的交互体验,同时确保繁重的计算或渲染不会阻塞关键任务。
FormData 是 HTML5 提供的一个内置接口,常用于以编程方式构建和管理表单数据,尤其是在与服务器交互(如通过 fetch 或 XMLHttpRequest 提交表单)时。
它允许开发者轻松构建键值对形式的表单数据,同时支持文件上传等复杂操作。
创建 FormData 对象
从已有表单元素构建:
<form> 元素创建 FormData,自动收集表单中所有具有 name 属性的字段数据。const form = document.querySelector("form");
const formData = new FormData(form);
空表单数据对象:
FormData 对象,并动态添加数据。const formData = new FormData();
formData.append("key", "value");
常用方法
1. append
向 FormData 对象添加一个键值对。
formData.append(name, value, filename);
name: 字段名(字符串)。value: 字段值(字符串、Blob 或 File)。filename(可选):用于文件字段,指定文件名。formData.append("username", "JohnDoe");
formData.append("profile", fileInput.files[0], "profile.png"); // 上传文件
2. get
获取指定键的值(如果有多个值,则只返回第一个)。
const value = formData.get(name);
console.log(value); // "JohnDoe"
3. getAll
获取指定键的所有值(如果键有多个值)。
const values = formData.getAll(name);
console.log(values); // ["value1", "value2"]
4. set
设置指定键的值(如果键已存在,则覆盖)。
formData.set("username", "JaneDoe");
5. delete
删除指定键及其所有值。
formData.delete("username");
6. has
检查指定键是否存在。
console.log(formData.has("username")); // true 或 false
7. entries
返回一个迭代器,遍历所有键值对。
for (const [key, value] of formData.entries()) {
console.log(key, value);
}
8. keys
返回一个迭代器,遍历所有键。
for (const key of formData.keys()) {
console.log(key);
}
9. values
返回一个迭代器,遍历所有值。
for (const value of formData.values()) {
console.log(value);
}
与 fetch 结合提交表单
使用 FormData 和 fetch 一起提交表单数据。
const form = document.querySelector("form");
const formData = new FormData(form);
fetch("https://example.com/submit", {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((data) => {
console.log("Success:", data);
})
.catch((error) => {
console.error("Error:", error);
});
优点
Content-Type: multipart/form-data。示例:动态构建和提交 FormData
const formData = new FormData();
// 动态添加字段
formData.append("username", "JohnDoe");
formData.append("age", 30);
// 添加文件
const fileInput = document.querySelector("#file");
formData.append("profile", fileInput.files[0]);
// 提交数据
fetch("https://example.com/submit", {
method: "POST",
body: formData,
}).then((response) => {
console.log("Form submitted successfully!");
});
文件上传场景
利用 FormData 上传文件时,文件字段的值为 File 或 Blob 对象。
HTML 示例
<form id="uploadForm">
<input type="file" name="photo" />
<button type="submit">Upload</button>
</form>
JavaScript 示例
const form = document.getElementById("uploadForm");
form.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(form);
fetch("https://example.com/upload", {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((data) => {
console.log("Upload successful:", data);
})
.catch((error) => {
console.error("Upload error:", error);
});
});
注意事项
文件上传支持
FormData 支持文件上传,将文件通过 <input type="file"> 添加到表单中即可。Content-Type,fetch 会自动处理。跨域问题
调试 FormData
FormData 无法直接通过 console.log 查看内容,需使用迭代方法(如 entries())。序列化为字符串
FormData 序列化为 URL 编码的字符串(如 application/x-www-form-urlencoded),可以使用以下方法:const params = new URLSearchParams(formData);
console.log(params.toString());
总结
FormData 是管理和提交表单数据的利器,尤其在涉及文件上传的场景下非常方便。fetch)结合,可以轻松构建强大的表单提交功能。append、set、get 等),FormData 提供了灵活的数据管理方式,简化了复杂表单数据的处理。useActionState 是 React 19 引入的一个新的 Hook,主要用于处理表单提交状态和结果。
import { useActionState } from "react";
function MyForm() {
const [formState, formAction] = useActionState(async (formData) => {
// 处理表单提交
const result = await submitData(formData);
return result;
});
return (
<form action={formAction}>
{formState.pending && <p>提交中...</p>}
{formState.error && <p>错误:{formState.error.message}</p>}
{formState.data && <p>提交成功!</p>}
<button type="submit">提交</button>
</form>
);
}
useActionState 返回的状态对象包含以下属性:
{
pending: boolean, // 是否正在处理
data: any, // 成功时的返回数据
error: Error, // 错误信息
status: string // 当前状态:'idle' | 'pending' | 'success' | 'error'
}
function RegistrationForm() {
const [state, formAction] = useActionState(async (formData) => {
// 模拟 API 调用
if (formData.get("email").includes("test")) {
throw new Error("测试邮箱不允许注册");
}
return { message: "注册成功" };
});
return (
<form action={formAction}>
<div>
<input name="email" type="email" required />
<input name="password" type="password" required />
</div>
{/* 状态展示 */}
{state.pending && <div>注册中...</div>}
{state.error && <div className="error">{state.error.message}</div>}
{state.data && <div className="success">{state.data.message}</div>}
<button type="submit" disabled={state.pending}>
{state.pending ? "处理中..." : "注册"}
</button>
</form>
);
}
主要优势
状态管理自动化
更简洁的代码
更好的用户体验
进阶用法
function ComplexForm() {
const [state, formAction] = useActionState(
async (formData, { signal }) => {
// 支持中断请求
const response = await fetch("/api/submit", {
method: "POST",
body: formData,
signal,
});
if (!response.ok) {
throw new Error("提交失败");
}
return await response.json();
},
{
// 可选配置项
initialState: { data: null },
optimisticUpdate: (formData) => ({
// 乐观更新
message: "正在处理...",
}),
}
);
return <form action={formAction}>{/* 表单内容 */}</form>;
}
注意事项
useActionState 必须在组件顶层调用
useTransition 使用来优化用户体验这个新的 Hook 大大简化了表单处理和状态管理的复杂度,使得开发者可以更专注于业务逻辑的实现,而不是重复编写状态管理的模板代码。
useOptimistic 是 React 19 中新增的一个 Hook,专门设计用于处理乐观更新的场景。乐观更新是一种优化技术,用于在用户与界面交互时,立即更新 UI,从而提供更流畅的体验,而无需等待后端的响应。useOptimistic 使得这一过程更加简单和直观。
基本用法
useOptimistic 提供了一种声明式的方法来管理乐观状态。它的基本形式如下:
const [optimisticState, addOptimisticUpdate] = useOptimistic(
initialState,
reducer
);
initialState:初始状态,通常是你的 UI 的默认状态。reducer:一个函数,用于描述状态如何根据特定的操作发生变化。类似于 useReducer 中的 reducer。参数详解
initialState:
reducer:
state)action)返回值
useOptimistic 返回一个数组,包含两个元素:
optimisticState:
addOptimisticUpdate:
reducer,并立即更新 optimisticState。示例
以下是一个简单的乐观更新示例:用户点击按钮时立即更新计数,而无需等待服务器的响应。
import React from "react";
import { useOptimistic } from "react";
function Counter() {
const [count, addOptimisticUpdate] = useOptimistic(0, (state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
});
const handleIncrement = async () => {
// 乐观更新
addOptimisticUpdate({ type: "increment" });
try {
// 模拟向服务器发送请求
await fetch("/api/increment", { method: "POST" });
} catch (error) {
console.error("服务器请求失败,可能需要回滚");
// 根据需要回滚
}
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>+1</button>
</div>
);
}
核心特点
简单声明式 API:
reducer 集中管理,易于维护。自动与异步操作结合:
灵活的状态管理:
常见用例
表单提交:
列表操作:
计数操作:
注意事项
回滚逻辑:
数据一致性:
不要滥用:
总结
useOptimistic 是一个非常强大的工具,能显著简化乐观更新的实现,同时提升用户体验。通过声明式 API 和 reducer 管理状态变化,你可以快速构建高效、响应迅速的交互式应用。
React Compiler Healthcheck
React Compiler Healthcheck 是一个工具,用于帮助开发者分析代码库是否符合 React Compiler(如 React Forget 编译器)的优化要求,以及检测可能影响自动化优化的代码问题。它的目标是提升代码质量并确保编译器可以正确处理代码,最大化 React 应用的性能潜力。
React Compiler Healthcheck 的核心功能
自动分析代码库:
识别常见问题:
生成健康报告:
与 ESLint 集成:
安装与使用
安装
React Compiler Healthcheck 可能是独立的工具或集成在 React DevTools、ESLint 插件中,安装方式如下:
npm install -D react-compiler-healthcheck@beta eslint-plugin-react-compiler@beta
或者使用 Yarn:
yarn add -D react-compiler-healthcheck@beta eslint-plugin-react-compiler@beta
运行检查 使用命令行工具运行健康检查:
npx react-compiler-healthcheck
此命令会扫描整个代码库,并输出一份详细的检查报告。
配置 ESLint 通过添加 ESLint 插件,可以在编辑器中实时查看健康检查的反馈:
在 .eslintrc.js 文件中配置:
module.exports = {
plugins: ["react-compiler"],
rules: {
"react-compiler/no-memo-issues": "warn", // 示例规则
},
};
编辑器中会显示潜在问题的警告或错误信息。
健康检查的关键检查点
依赖项问题
useEffect、useMemo 或 useCallback 中依赖项错误可能导致性能问题或不必要的重新渲染。复杂状态管理
组件重渲染
React.memo 包裹的组件。代码可预测性
输出示例
当运行健康检查工具后,会生成如下报告:
React Compiler Healthcheck Report
=================================
✔ Scan completed: 100 files analyzed.
Issues found:
1. useEffect dependency array incomplete. (src/components/MyComponent.js:15)
Suggestion: Add [props.data] to the dependency array.
2. React.memo missing for heavy component. (src/components/HeavyList.js:32)
Suggestion: Wrap component with React.memo to prevent unnecessary re-renders.
3. Unnecessary state updates in nested loops. (src/utils/calculate.js:10)
Suggestion: Simplify state management logic.
Overall Health Score: 75/100
Recommendation: Address high-priority issues to unlock React Compiler optimizations.
工具的优势
提升代码质量:
开发者友好:
优化 React 应用性能:
注意事项
Beta 阶段:
React Compiler Healthcheck 当前可能仍处于 Beta 测试阶段,某些功能可能不稳定。团队协作:
与现有工具结合:
总结
React Compiler Healthcheck 是一款专为优化 React 代码库设计的工具。通过分析代码中可能影响 React Compiler 自动优化的潜在问题,并提供修复建议,它可以帮助开发者构建更高效、更易维护的 React 应用。随着 React Compiler 的逐步普及,这一工具将成为开发者日常工作的重要组成部分。
React Compiler 是 React 官方推出的新一代工具,旨在编译和优化 React 代码,以提高应用的性能和开发效率。它通过分析和转换代码,在编译阶段引入自动优化机制,使得开发者无需手动调整代码来实现性能优化。
React Compiler 的目标是减少运行时开销,提升渲染效率,并为开发者提供更好的代码开发体验。
React Compiler 的核心功能
自动记忆化(Automatic Memoization)
原理:编译器可以自动检测组件、状态和钩子是否需要记忆化,从而避免不必要的重新计算和重新渲染。
React.memo、useMemo 或 useCallback。示例:
原始代码:
function MyComponent({ count }) {
return <div>{count}</div>;
}
编译后:
function MyComponent({ count }) {
const cache = _c(1); // 缓存初始化
let jsxElement;
if (cache[0] !== count) {
jsxElement = <div>{count}</div>;
cache[0] = count; // 更新缓存
cache[1] = jsxElement;
} else {
jsxElement = cache[1]; // 复用缓存
}
return jsxElement;
}
编译器自动将组件的输出缓存起来,避免了重复生成 JSX。
依赖项分析
原理:编译器能静态分析钩子(如 useEffect 和 useMemo)的依赖项,确保依赖声明的完整性。
示例:
原始代码:
useEffect(() => {
console.log("Count updated:", count);
}, []);
编译后:
useEffect(() => {
console.log("Count updated:", count);
}, [count]); // 自动添加依赖项
静态优化
原理:识别代码中的静态部分,在编译阶段将其优化为常量或缓存结构。
示例:
原始代码:
function App() {
return <div>Hello, World!</div>;
}
编译后:
const StaticContent = <div>Hello, World!</div>;
function App() {
return StaticContent; // 静态内容直接复用
}
智能状态管理
原理:对组件状态的使用进行静态分析,优化状态更新逻辑和依赖关系。
React Compiler 的特点
开发者无感知
兼容现有代码
与运行时配合
React.memo、useMemo 等)协同工作,进一步提高应用性能。易于调试
eslint-plugin-react-compiler),帮助开发者在编辑器中检测和优化代码。React Compiler 的使用方法
npm install -D babel-plugin-react-compiler
.babelrc 文件中添加:{
"plugins": ["react-compiler"]
}
React Compiler 的优劣势
优点
React.memo 或 useMemo,减少优化代码的复杂度。缺点
未来展望
React Compiler 是 React 性能优化生态的重要组成部分,未来可能进一步扩展功能,例如:
总结
React Compiler 是一项革命性工具,通过在编译阶段对 React 代码进行深度优化,帮助开发者简化性能优化工作,提高应用的效率和用户体验。它让开发者专注于业务逻辑,而不是陷入复杂的优化代码中,是未来 React 应用开发的重要方向。