React 19 升级指南

2024年4月25日,作者 Ricky Hanlon

🌐 April 25, 2024 by Ricky Hanlon


添加到 React 19 的改进需要一些重大更改,但我们努力使升级尽可能顺利,并且我们预计这些更改不会影响大多数应用。

🌐 The improvements added to React 19 require some breaking changes, but we’ve worked to make the upgrade as smooth as possible, and we don’t expect the changes to impact most apps.

注意

React 18.3 也已发布

🌐 React 18.3 has also been published

为了帮助更轻松地升级到 React 19,我们发布了一个 react@18.3 版本,该版本与 18.2 完全相同,但增加了对已弃用 API 的警告以及 React 19 所需的其他更改。

🌐 To help make the upgrade to React 19 easier, we’ve published a react@18.3 release that is identical to 18.2 but adds warnings for deprecated APIs and other changes that are needed for React 19.

我们建议先升级到 React 18.3,以帮助在升级到 React 19 之前识别任何问题。

🌐 We recommend upgrading to React 18.3 first to help identify any issues before upgrading to React 19.

有关 18.3 的更改列表,请参阅 发行说明

🌐 For a list of changes in 18.3 see the Release Notes.

在这篇文章中,我们将指导你完成升级到 React 19 的步骤:

🌐 In this post, we will guide you through the steps for upgrading to React 19:

如果你想帮助我们测试 React 19,请按照此升级指南中的步骤操作,并报告你遇到的任何问题。有关 React 19 添加的新功能列表,请参阅React 19 发布文章

🌐 If you’d like to help us test React 19, follow the steps in this upgrade guide and report any issues you encounter. For a list of new features added to React 19, see the React 19 release post.


正在安装

🌐 Installing

注意

现在需要新的 JSX 转换

🌐 New JSX Transform is now required

我们在2020年引入了新的JSX转换,以改善包大小并在使用JSX时无需导入React。在React 19中,我们正在添加额外的改进,例如将ref用作prop以及需要新转换的JSX速度改进。

🌐 We introduced a new JSX transform in 2020 to improve bundle size and use JSX without importing React. In React 19, we’re adding additional improvements like using ref as a prop and JSX speed improvements that require the new transform.

如果未启用新的转换,你将看到此警告:

🌐 If the new transform is not enabled, you will see this warning:

Console
Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform

我们预计大多数应用不会受到影响,因为该转换在大多数环境中已经启用。有关如何手动升级的说明,请参阅公告帖子

🌐 We expect most apps will not be affected since the transform is enabled in most environments already. For manual instructions on how to upgrade, please see the announcement post.

要安装最新版本的 React 和 React DOM:

🌐 To install the latest version of React and React DOM:

npm install --save-exact react@^19.0.0 react-dom@^19.0.0

或者,如果你使用 Yarn:

🌐 Or, if you’re using Yarn:

yarn add --exact react@^19.0.0 react-dom@^19.0.0

如果你正在使用 TypeScript,你还需要更新类型。

🌐 If you’re using TypeScript, you also need to update the types.

npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0

或者,如果你使用 Yarn:

🌐 Or, if you’re using Yarn:

yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0

我们还包括了最常见替换的代码修改工具。请参见下面的 TypeScript 变更

🌐 We’re also including a codemod for the most common replacements. See TypeScript changes below.

代码修改器

🌐 Codemods

为了帮助升级,我们与 codemod.com 团队合作,发布了 codemod,这些 codemod 可以自动将你的代码更新为 React 19 中的许多新 API 和模式。

🌐 To help with the upgrade, we’ve worked with the team at codemod.com to publish codemods that will automatically update your code to many of the new APIs and patterns in React 19.

所有的 codemod 都可以在 react-codemod 仓库 中找到,并且 Codemod 团队已经加入以帮助维护这些 codemod。要运行这些 codemod,我们推荐使用 codemod 命令,而不是 react-codemod,因为它运行更快,处理更复杂的代码迁移,并且对 TypeScript 提供更好的支持。

🌐 All codemods are available in the react-codemod repo and the Codemod team have joined in helping maintain the codemods. To run these codemods, we recommend using the codemod command instead of the react-codemod because it runs faster, handles more complex code migrations, and provides better support for TypeScript.

注意

运行所有 React 19 代码转换工具

🌐 Run all React 19 codemods

使用 React 19 codemod 配方运行本指南中列出的所有 codemod:

🌐 Run all codemods listed in this guide with the React 19 codemod recipe:

npx codemod@latest react/19/migration-recipe

这将运行来自 react-codemod 的以下代码修改:

🌐 This will run the following codemods from react-codemod:

这不包括 TypeScript 的更改。请参见下面的 TypeScript 更改

🌐 This does not include the TypeScript changes. See TypeScript changes below.

包含代码转换的更改包括以下命令。

🌐 Changes that include a codemod include the command below.

有关所有可用 codemod 的列表,请参见 react-codemod 仓库

🌐 For a list of all available codemods, see the react-codemod repo.

重大更改

🌐 Breaking changes

渲染中的错误不会被重新抛出

🌐 Errors in render are not re-thrown

在以前的 React 版本中,渲染期间抛出的错误会被捕获并重新抛出。在开发环境中,我们还会记录到 console.error,导致错误日志重复。

🌐 In previous versions of React, errors thrown during render were caught and rethrown. In DEV, we would also log to console.error, resulting in duplicate error logs.

在 React 19 中,我们已经改进了错误处理方式,通过不重新抛出错误来减少重复:

🌐 In React 19, we’ve improved how errors are handled to reduce duplication by not re-throwing:

  • 未捕获错误:未被错误边界捕获的错误会被报告到 window.reportError
  • 捕获的错误:由错误边界捕获的错误会被报告给 console.error

此更改不应影响大多数应用,但如果你的生产错误报告依赖于错误被重新抛出,你可能需要更新你的错误处理。为支持这一点,我们在 createRoothydrateRoot 中添加了用于自定义错误处理的新方法:

🌐 This change should not impact most apps, but if your production error reporting relies on errors being re-thrown, you may need to update your error handling. To support this, we’ve added new methods to createRoot and hydrateRoot for custom error handling:

const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... log error report
},
onCaughtError: (error, errorInfo) => {
// ... log error report
}
});

欲了解更多信息,请参阅 createRoothydrateRoot 的文档。

🌐 For more info, see the docs for createRoot and hydrateRoot.

已移除弃用的 React API

🌐 Removed deprecated React APIs

已移除:函数的 propTypesdefaultProps

🌐 Removed: propTypes and defaultProps for functions

PropTypes 已在 2017年4月 (v15.5.0) 被弃用。

在 React 19 中,我们将从 React 包中移除 propType 检查,使用它们将被静默忽略。如果你正在使用 propTypes,我们建议迁移到 TypeScript 或其他类型检查解决方案。

🌐 In React 19, we’re removing the propType checks from the React package, and using them will be silently ignored. If you’re using propTypes, we recommend migrating to TypeScript or another type-checking solution.

我们也正在从函数组件中移除 defaultProps,用 ES6 默认参数代替。类组件将继续支持 defaultProps,因为没有 ES6 替代方案。

🌐 We’re also removing defaultProps from function components in place of ES6 default parameters. Class components will continue to support defaultProps since there is no ES6 alternative.

// Before
import PropTypes from 'prop-types';

function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: 'Hello, world!',
};
// After
interface Props {
text?: string;
}
function Heading({text = 'Hello, world!'}: Props) {
return <h1>{text}</h1>;
}

注意

使用以下方式将 propTypes 代码修改为 TypeScript:

🌐 Codemod propTypes to TypeScript with:

npx codemod@latest react/prop-types-typescript

已移除:使用 contextTypesgetChildContext 的遗留上下文

🌐 Removed: Legacy Context using contextTypes and getChildContext

旧版上下文在2018年10月(v16.6.0)已被弃用。

🌐 Legacy Context was deprecated in October 2018 (v16.6.0).

遗留上下文仅在使用 contextTypesgetChildContext API 的类组件中可用,由于容易忽略的细微错误,它被 contextType 所取代。在 React 19 中,我们将移除遗留上下文,以使 React 略微更小更快。

🌐 Legacy Context was only available in class components using the APIs contextTypes and getChildContext, and was replaced with contextType due to subtle bugs that were easy to miss. In React 19, we’re removing Legacy Context to make React slightly smaller and faster.

如果你仍然在类组件中使用旧的上下文,你需要迁移到新的 contextType API:

🌐 If you’re still using Legacy Context in class components, you’ll need to migrate to the new contextType API:

// Before
import PropTypes from 'prop-types';

class Parent extends React.Component {
static childContextTypes = {
foo: PropTypes.string.isRequired,
};

getChildContext() {
return { foo: 'bar' };
}

render() {
return <Child />;
}
}

class Child extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
};

render() {
return <div>{this.context.foo}</div>;
}
}
// After
const FooContext = React.createContext();

class Parent extends React.Component {
render() {
return (
<FooContext value='bar'>
<Child />
</FooContext>
);
}
}

class Child extends React.Component {
static contextType = FooContext;

render() {
return <div>{this.context}</div>;
}
}

已移除:字符串引用

🌐 Removed: string refs

字符串引用在 2018 年 3 月(v16.3.0) 已被弃用。

🌐 String refs were deprecated in March, 2018 (v16.3.0).

在被 ref 回调取代之前,类组件支持字符串 ref,但由于 多种缺点 而被弃用。在 React 19 中,我们将移除字符串 ref,以使 React 更简单、更易于理解。

🌐 Class components supported string refs before being replaced by ref callbacks due to multiple downsides. In React 19, we’re removing string refs to make React simpler and easier to understand.

如果你仍在类组件中使用字符串 ref,你需要迁移到 ref 回调:

🌐 If you’re still using string refs in class components, you’ll need to migrate to ref callbacks:

// Before
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}

render() {
return <input ref='input' />;
}
}
// After
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}

render() {
return <input ref={input => this.input = input} />;
}
}

注意

使用 ref 回调转换字符串引用:

🌐 Codemod string refs with ref callbacks:

npx codemod@latest react/19/replace-string-ref

已移除:模块模式工厂

🌐 Removed: Module pattern factories

模块模式工厂在2019年8月 (v16.9.0) 被弃用。

🌐 Module pattern factories were deprecated in August 2019 (v16.9.0).

这种模式很少使用,支持它会导致 React 的体积略大且运行速度略慢。在 React 19 中,我们将移除对模块模式工厂的支持,你需要迁移到普通函数:

🌐 This pattern was rarely used and supporting it causes React to be slightly larger and slower than necessary. In React 19, we’re removing support for module pattern factories, and you’ll need to migrate to regular functions:

// Before
function FactoryComponent() {
return { render() { return <div />; } }
}
// After
function FactoryComponent() {
return <div />;
}

已移除:React.createFactory

🌐 Removed: React.createFactory

createFactory 已在 2020年2月 (v16.13.0) 被弃用。

在广泛支持 JSX 之前,使用 createFactory 很常见,但如今很少使用,可以用 JSX 代替。在 React 19 中,我们将移除 createFactory,你需要迁移到 JSX:

🌐 Using createFactory was common before broad support for JSX, but it’s rarely used today and can be replaced with JSX. In React 19, we’re removing createFactory and you’ll need to migrate to JSX:

// Before
import { createFactory } from 'react';

const button = createFactory('button');
// After
const button = <button />;

已移除:react-test-renderer/shallow

🌐 Removed: react-test-renderer/shallow

在 React 18 中,我们更新了 react-test-renderer/shallow 以重新导出 react-shallow-renderer。在 React 19 中,我们将移除 react-test-render/shallow,建议直接安装该包:

🌐 In React 18, we updated react-test-renderer/shallow to re-export react-shallow-renderer. In React 19, we’re removing react-test-render/shallow to prefer installing the package directly:

npm install react-shallow-renderer --save-dev
- import ShallowRenderer from 'react-test-renderer/shallow';
+ import ShallowRenderer from 'react-shallow-renderer';

注意

请重新考虑浅渲染

🌐 Please reconsider shallow rendering

浅层渲染依赖于 React 内部机制,可能会阻碍你未来的升级。我们建议将你的测试迁移到 @testing-library/react@testing-library/react-native

🌐 Shallow rendering depends on React internals and can block you from future upgrades. We recommend migrating your tests to @testing-library/react or @testing-library/react-native.

已移除已弃用的 React DOM API

🌐 Removed deprecated React DOM APIs

已移除:react-dom/test-utils

🌐 Removed: react-dom/test-utils

我们已将 actreact-dom/test-utils 移动到 react 包中:

🌐 We’ve moved act from react-dom/test-utils to the react package:

Console
ReactDOMTestUtils.act is deprecated in favor of React.act. Import act from react instead of react-dom/test-utils. See https://react.dev/warnings/react-dom-test-utils for more info.

要解决此警告,你可以从 react 导入 act

🌐 To fix this warning, you can import act from react:

- import {act} from 'react-dom/test-utils'
+ import {act} from 'react';

所有其他的 test-utils 函数已被移除。这些工具不常用,并且过于容易让人依赖组件和 React 的底层实现细节。在 React 19 中,调用这些函数将会报错,并且它们的导出将在未来的版本中被移除。

🌐 All other test-utils functions have been removed. These utilities were uncommon, and made it too easy to depend on low level implementation details of your components and React. In React 19, these functions will error when called and their exports will be removed in a future version.

请参阅警告页面了解替代方案。

🌐 See the warning page for alternatives.

注意

ReactDOMTestUtils.act 代码修改为 React.act

🌐 Codemod ReactDOMTestUtils.act to React.act:

npx codemod@latest react/19/replace-act-import

已移除:ReactDOM.render

🌐 Removed: ReactDOM.render

ReactDOM.render 已在 2022年3月 (v18.0.0) 中弃用。在 React 19 中,我们将移除 ReactDOM.render,你需要迁移到使用 ReactDOM.createRoot

// Before
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));

// After
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

注意

ReactDOM.render 代码修改为 ReactDOMClient.createRoot

🌐 Codemod ReactDOM.render to ReactDOMClient.createRoot:

npx codemod@latest react/19/replace-reactdom-render

已移除:ReactDOM.hydrate

🌐 Removed: ReactDOM.hydrate

ReactDOM.hydrate 已在 2022年3月 (v18.0.0) 弃用。在 React 19 中,我们将移除 ReactDOM.hydrate,你需要迁移到使用 ReactDOM.hydrateRoot

// Before
import {hydrate} from 'react-dom';
hydrate(<App />, document.getElementById('root'));

// After
import {hydrateRoot} from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);

注意

ReactDOM.hydrate 代码修改为 ReactDOMClient.hydrateRoot

🌐 Codemod ReactDOM.hydrate to ReactDOMClient.hydrateRoot:

npx codemod@latest react/19/replace-reactdom-render

已移除:unmountComponentAtNode

🌐 Removed: unmountComponentAtNode

ReactDOM.unmountComponentAtNode 已在 2022年3月 (v18.0.0) 弃用。在 React 19 中,你需要迁移使用 root.unmount()

// Before
unmountComponentAtNode(document.getElementById('root'));

// After
root.unmount();

更多内容请参见 root.unmount()createRoothydrateRoot

🌐 For more see root.unmount() for createRoot and hydrateRoot.

注意

unmountComponentAtNode 代码修改为 root.unmount

🌐 Codemod unmountComponentAtNode to root.unmount:

npx codemod@latest react/19/replace-reactdom-render

已移除:ReactDOM.findDOMNode

🌐 Removed: ReactDOM.findDOMNode

ReactDOM.findDOMNode 已在 2018 年 10 月(v16.6.0)弃用

我们正在移除 findDOMNode,因为它是一个遗留的应急方法,执行缓慢、在重构时容易出错、只返回第一个子元素,并且破坏了抽象层次(更多信息见 这里)。你可以用 DOM 引用 来替换 ReactDOM.findDOMNode

🌐 We’re removing findDOMNode because it was a legacy escape hatch that was slow to execute, fragile to refactoring, only returned the first child, and broke abstraction levels (see more here). You can replace ReactDOM.findDOMNode with DOM refs:

// Before
import {findDOMNode} from 'react-dom';

function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);

return <input defaultValue="Hello" />;
}
// After
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);

return <input ref={ref} defaultValue="Hello" />
}

新的弃用项

🌐 New deprecations

已弃用:element.ref

🌐 Deprecated: element.ref

React 19 支持 ref 作为属性,因此我们正在弃用 element.ref,以取代 element.props.ref

🌐 React 19 supports ref as a prop, so we’re deprecating the element.ref in place of element.props.ref.

访问 element.ref 将会警告:

🌐 Accessing element.ref will warn:

Console
Accessing element.ref is no longer supported. ref is now a regular prop. It will be removed from the JSX Element type in a future release.

已弃用:react-test-renderer

🌐 Deprecated: react-test-renderer

我们正在弃用 react-test-renderer,因为它实现了与用户使用的环境不匹配的渲染器环境,促进了对实现细节的测试,并依赖于对 React 内部的内省。

🌐 We are deprecating react-test-renderer because it implements its own renderer environment that doesn’t match the environment users use, promotes testing implementation details, and relies on introspection of React’s internals.

测试渲染器是在出现像 React 测试库 这样更可行的测试策略之前创建的,我们现在建议改用现代测试库。

🌐 The test renderer was created before there were more viable testing strategies available like React Testing Library, and we now recommend using a modern testing library instead.

在 React 19 中,react-test-renderer 会记录一个弃用警告,并已切换到并发渲染。我们建议将你的测试迁移到 @testing-library/react@testing-library/react-native,以获得现代且支持良好的测试体验。

🌐 In React 19, react-test-renderer logs a deprecation warning, and has switched to concurrent rendering. We recommend migrating your tests to @testing-library/react or @testing-library/react-native for a modern and well supported testing experience.

显著变化

🌐 Notable changes

StrictMode 更改

🌐 StrictMode changes

React 19 包含对严格模式的若干修复和改进。

🌐 React 19 includes several fixes and improvements to Strict Mode.

在开发中使用严格模式进行双重渲染时,useMemouseCallback 会在第二次渲染时重用第一次渲染的记忆结果。已经兼容严格模式的组件不应该注意到行为上的差异。

🌐 When double rendering in Strict Mode in development, useMemo and useCallback will reuse the memoized results from the first render during the second render. Components that are already Strict Mode compatible should not notice a difference in behavior.

与所有严格模式行为一样,这些功能旨在主动在开发过程中暴露组件中的错误,以便你在将其发布到生产环境之前进行修复。例如,在开发过程中,严格模式会在初始挂载时双重调用 ref 回调函数,以模拟挂载的组件被 Suspense 回退组件替换时发生的情况。

🌐 As with all Strict Mode behaviors, these features are designed to proactively surface bugs in your components during development so you can fix them before they are shipped to production. For example, during development, Strict Mode will double-invoke ref callback functions on initial mount, to simulate what happens when a mounted component is replaced by a Suspense fallback.

Suspense的改进

🌐 Improvements to Suspense

在 React 19 中,当一个组件挂起时,React 会立即提交最近的 Suspense 边界的回退,而无需等待整个兄弟节点树渲染完成。在回退提交之后,React 会为挂起的兄弟节点安排另一次渲染,以便在其余的树中“预热”懒加载请求:

🌐 In React 19, when a component suspends, React will immediately commit the fallback of the nearest Suspense boundary without waiting for the entire sibling tree to render. After the fallback commits, React schedules another render for the suspended siblings to “pre-warm” lazy requests in the rest of the tree:

Diagram showing a tree of three components, one parent labeled Accordion and two children labeled Panel. Both Panel components contain isActive with value false.
Diagram showing a tree of three components, one parent labeled Accordion and two children labeled Panel. Both Panel components contain isActive with value false.

以前,当一个组件挂起时,挂起的兄弟组件会被渲染,然后回退内容会被提交。

🌐 Previously, when a component suspended, the suspended siblings were rendered and then the fallback was committed.

The same diagram as the previous, with the isActive of the first child Panel component highlighted indicating a click with the isActive value set to true. The second Panel component still contains value false.
The same diagram as the previous, with the isActive of the first child Panel component highlighted indicating a click with the isActive value set to true. The second Panel component still contains value false.

在 React 19 中,当一个组件挂起时,会先提交备用内容,然后渲染挂起的兄弟组件。

🌐 In React 19, when a component suspends, the fallback is committed and then the suspended siblings are rendered.

此更改意味着 Suspense 回退显示更快,同时仍在挂起的树中预热延迟请求。

🌐 This change means Suspense fallbacks display faster, while still warming lazy requests in the suspended tree.

已移除 UMD 构建

🌐 UMD builds removed

过去,UMD 被广泛用作一种方便的方式来在没有构建步骤的情况下加载 React。现在,有一些现代的替代方法可以在 HTML 文档中将模块作为脚本加载。从 React 19 开始,React 将不再生成 UMD 构建,以减少其测试和发布过程的复杂性。

🌐 UMD was widely used in the past as a convenient way to load React without a build step. Now, there are modern alternatives for loading modules as scripts in HTML documents. Starting with React 19, React will no longer produce UMD builds to reduce the complexity of its testing and release process.

要通过脚本标签加载 React 19,我们建议使用基于 ESM 的 CDN,例如 esm.sh

🌐 To load React 19 with a script tag, we recommend using an ESM-based CDN such as esm.sh.

<script type="module">
import React from "https://esm.sh/react@19/?dev"
import ReactDOMClient from "https://esm.sh/react-dom@19/client?dev"
...
</script>

依赖 React 内部实现的库可能会阻碍升级

🌐 Libraries depending on React internals may block upgrades

此版本包括对 React 内部的更改,这可能会影响忽视我们不要使用内部如 SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 的呼吁的库。这些更改对于在 React 19 中引入改进是必要的,并且不会破坏遵循我们指南的库。

🌐 This release includes changes to React internals that may impact libraries that ignore our pleas to not use internals like SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. These changes are necessary to land improvements in React 19, and will not break libraries that follow our guidelines.

根据我们的版本管理政策,这些更新未被列为重大更改,我们也没有包含关于如何升级它们的文档。建议是移除任何依赖内部实现的代码。

🌐 Based on our Versioning Policy, these updates are not listed as breaking changes, and we are not including docs for how to upgrade them. The recommendation is to remove any code that depends on internals.

为了反映使用内部元素的影响,我们已将 SECRET_INTERNALS 后缀重命名为:

🌐 To reflect the impact of using internals, we have renamed the SECRET_INTERNALS suffix to:

_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE

在未来,我们将更积极地阻止从 React 访问内部内容,以减少使用并确保用户在升级时不会受到阻碍。

🌐 In the future we will more aggressively block accessing internals from React to discourage usage and ensure users are not blocked from upgrading.

TypeScript 变化

🌐 TypeScript changes

已移除已弃用的 TypeScript 类型

🌐 Removed deprecated TypeScript types

我们已根据 React 19 中移除的 API 清理了 TypeScript 类型。部分已移除的类型已移到更相关的包中,其他类型则不再需要用于描述 React 的行为。

🌐 We’ve cleaned up the TypeScript types based on the removed APIs in React 19. Some of the removed have types been moved to more relevant packages, and others are no longer needed to describe React’s behavior.

注意

我们已经发布了types-react-codemod来迁移大多数与类型相关的重大更改:

npx types-react-codemod@latest preset-19 ./path-to-app

如果你对 element.props 有大量不安全的访问,你可以运行这个额外的代码转化工具:

🌐 If you have a lot of unsound access to element.props, you can run this additional codemod:

npx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files

查看 types-react-codemod 以获取受支持的替换列表。如果你觉得某个 codemod 缺失,可以在 缺失的 React 19 codemods 列表 中跟踪。

🌐 Check out types-react-codemod for a list of supported replacements. If you feel a codemod is missing, it can be tracked in the list of missing React 19 codemods.

ref 次清理需要

🌐 ref cleanups required

此更改包含在 react-19 codemod 预设中,作为 no-implicit-ref-callback-return

🌐 This change is included in the react-19 codemod preset as no-implicit-ref-callback-return .

由于引入了 ref 清理函数,从 ref 回调返回其他任何内容现在都将被 TypeScript 拒绝。通常的解决方法是停止使用隐式返回:

🌐 Due to the introduction of ref cleanup functions, returning anything else from a ref callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns:

- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />

原始代码返回了 HTMLDivElement 的实例,而 TypeScript 无法判断这是否应该是一个清理函数。

🌐 The original code returned the instance of the HTMLDivElement and TypeScript wouldn’t know if this was supposed to be a cleanup function or not.

useRef 需要一个参数

🌐 useRef requires an argument

此更改包含在 react-19 codemod 预设中,作为 refobject-defaults

🌐 This change is included in the react-19 codemod preset as refobject-defaults.

长期以来,人们对 TypeScript 和 React 的工作方式抱怨 useRef。我们已经更改了类型,使得 useRef 现在需要一个参数。这显著简化了它的类型签名。它现在的行为将更像 createContext

🌐 A long-time complaint of how TypeScript and React work has been useRef. We’ve changed the types so that useRef now requires an argument. This significantly simplifies its type signature. It’ll now behave more like createContext.

// @ts-expect-error: Expected 1 argument but saw none
useRef();
// Passes
useRef(undefined);
// @ts-expect-error: Expected 1 argument but saw none
createContext();
// Passes
createContext(undefined);

这现在也意味着所有引用都是可变的。你将不再遇到因为使用 null 初始化引用而无法修改它的问题:

🌐 This now also means that all refs are mutable. You’ll no longer hit the issue where you can’t mutate a ref because you initialised it with null:

const ref = useRef<number>(null);

// Cannot assign to 'current' because it is a read-only property
ref.current = 1;

MutableRef 现在已被弃用,取而代之的是单一的 RefObject 类型,useRef 将始终返回该类型:

interface RefObject<T> {
current: T
}

declare function useRef<T>: RefObject<T>

useRef 仍然为 useRef<T>(null) 提供了一个方便的重载,它会自动返回 RefObject<T | null>。为了简化由于 useRef 所需参数而进行的迁移,添加了一个 useRef(undefined) 的方便重载,它会自动返回 RefObject<T | undefined>

查看 [RFC] Make all refs mutable 以了解关于此更改的先前讨论。

🌐 Check out [RFC] Make all refs mutable for prior discussions about this change.

ReactElement TypeScript 类型的更改

🌐 Changes to the ReactElement TypeScript type

此更改包含在 react-element-default-any-props 代码修改中.

🌐 This change is included in the react-element-default-any-props codemod.

如果将元素类型指定为 ReactElement,React 元素的 props 现在默认值为 unknown 而不是 any。如果你向 ReactElement 传递类型参数,则不会受到影响:

🌐 The props of React elements now default to unknown instead of any if the element is typed as ReactElement. This does not affect you if you pass a type argument to ReactElement:

type Example2 = ReactElement<{ id: string }>["props"];
// ^? { id: string }

但是如果你依赖默认设置,你现在必须处理 unknown

🌐 But if you relied on the default, you now have to handle unknown:

type Example = ReactElement["props"];
// ^? Before, was 'any', now 'unknown'

你应该只在拥有大量依赖不可靠元素属性访问的遗留代码时才需要它。元素自省只是作为一种应急手段存在,你应该通过显式的 any 明确表示你的属性访问是不可靠的。

🌐 You should only need it if you have a lot of legacy code relying on unsound access of element props. Element introspection only exists as an escape hatch, and you should make it explicit that your props access is unsound via an explicit any.

TypeScript 中的 JSX 命名空间

🌐 The JSX namespace in TypeScript

此更改包含在 react-19 codemod 预设中,作为 scoped-jsx

🌐 This change is included in the react-19 codemod preset as scoped-jsx

一个长期的请求是将我们的类型中的全局 JSX 命名空间移除,以 React.JSX 取而代之。这有助于防止全局类型的污染,从而避免使用 JSX 的不同 UI 库之间的冲突。

🌐 A long-time request is to remove the global JSX namespace from our types in favor of React.JSX. This helps prevent pollution of global types which prevents conflicts between different UI libraries that leverage JSX.

你现在需要将 JSX 命名空间的模块增强封装在 declare module "...." 中:

🌐 You’ll now need to wrap module augmentation of the JSX namespace in `declare module ”…”:

// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }

确切的模块说明符取决于你在 tsconfig.jsoncompilerOptions 中指定的 JSX 运行时:

🌐 The exact module specifier depends on the JSX runtime you specified in the compilerOptions of your tsconfig.json:

  • 对于 "jsx": "react-jsx" 它将是 react/jsx-runtime
  • 对于 "jsx": "react-jsxdev" 它将是 react/jsx-dev-runtime
  • 对于 "jsx": "react""jsx": "preserve",它将是 react

更好的 useReducer 类型定义

🌐 Better useReducer typings

useReducer 现在有了改进的类型推断,这要归功于 @mfp22

然而,这需要一个破坏性更改,其中 useReducer 不再接受完整的 reducer 类型作为类型参数,而是要么不需要任何参数(并依赖上下文类型),要么需要同时指定状态类型和动作类型。

🌐 However, this required a breaking change where useReducer doesn’t accept the full reducer type as a type parameter but instead either needs none (and rely on contextual typing) or needs both the state and action type.

新的最佳实践是不向 useReducer 传递类型参数。

🌐 The new best practice is not to pass type arguments to useReducer.

- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)

This may not work in edge cases where you can explicitly type the state and action, by passing in the Action in a tuple:

- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)

If you define the reducer inline, we encourage to annotate the function parameters instead:

- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)

This is also what you’d also have to do if you move the reducer outside of the useReducer call:

const reducer = (state: State, action: Action) => state;

更新日志

🌐 Changelog

其他重大变更

🌐 Other breaking changes

  • react-domsrchref 中的 JavaScript URL 错误 #26507
  • react-dom: 从 onRecoverableError 中移除 errorInfo.digest #28222
  • react-dom:移除 unstable_flushControlled #26397
  • react-dom:移除 unstable_createEventHandle #28271
  • react-dom:移除 unstable_renderSubtreeIntoContainer #28271
  • react-dom:移除 unstable_runWithPriority #28271
  • react-is:从 react-is 中移除已废弃的方法 28224

其他显著变化

🌐 Other notable changes

  • react:批量同步,默认和连续通道 #25700
  • react:不要预渲染被挂起组件的同级组件 #26380
  • react:检测由渲染阶段更新引起的无限更新循环 #26625
  • react-dom:popstate 中的过渡现在是同步的 #26025
  • react-dom:在服务器端渲染期间移除布局效果警告 #26395
  • react-dom:警告并且不要为 src/href 设置空字符串(锚点标签除外) #28124

有关完整的更改列表,请参阅 更新日志

🌐 For a full list of changes, please see the Changelog.


感谢 Andrew ClarkEli WhiteJack PopeJan KassensJosh StoryMatt CarrollNoah LemenSophie AlpertSebastian Silbermann 审阅和编辑这篇文章。

🌐 Thanks to Andrew Clark, Eli White, Jack Pope, Jan Kassens, Josh Story, Matt Carroll, Noah Lemen, Sophie Alpert, and Sebastian Silbermann for reviewing and editing this post.