作为 WordPress 开发人员,我们经常将自定义的 React 组件集成到我们的主题和插件中,以创建动态和响应式的用户界面。
随着 React 19 的即将发布,为可能影响我们现有代码库的更改和弃用做好准备至关重要。将于 7 月 16 日发布的 WordPress 6.6 包含 React 18.3。该版本与 18.2 几乎完全相同,但增加了对已废弃功能的警告,以帮助您为 React 19 做好准备。
解决这些弃用问题对于确保与 React 19 的兼容性至关重要,忽略它们可能会在 React 19 发布并包含在 WordPress 中时,导致您的自定义区块、插件或主题出现错误或问题。
本文概述了每种弃用情况,提供了代码示例,并指导您替换弃用的功能,以保持功能的流畅性。
注:为了帮助进行升级,React 团队与 Codemod 团队合作发布了一些 codemods,它们会自动将您的代码更新为 React 19 中的许多新 API 和模式。
所有的 codemod 都可以在 GitHub 上的 react-codemod repo 中找到。此外,我们还将附上每个弃用代码的 codemod 命令(如果可用),以帮助您自动更新代码。
React 中已删除的弃用功能
为了精简 React 库并鼓励最佳实践,我们删除了一些已废弃的 API 和功能。本节将介绍主要更改以及如何相应地更新代码。
1. 移除函数组件的 defaultProps
React 19 将移除函数组件的 defaultProps
,转而使用 ES6 默认参数。根据 WordPress 团队的说法,这种弃用最常用于插件和主题中。
作为 WordPress 开发人员,您可能会使用 defaultProps
为函数组件中的道具提供默认值,以确保即使没有传递某些道具,组件也能正常运行。
以下是使用 defaultProps
后的当前代码:
function CustomButton({ label, color }) { return <button style={{ backgroundColor: color }}>{ label }</button>; } CustomButton.defaultProps = { label: 'Click me', color: 'blue', };
在本示例中,CustomButton
组件的默认 label
和 color
值由 defaultProps
提供。在 React 19 中,这将引发一个警告错误,提醒您使用 ES6 默认参数。
以下是使用 ES6 默认参数的更新代码:
function CustomButton({ label = 'Click me', color = 'blue' }) { return <button style={{ backgroundColor: color }}>{ label }</button>; }
使用 ES6 默认参数,默认值现在直接出现在函数签名中,使代码更易于阅读和维护。
2. 移除函数组件的 propTypes
propTypes
已在 React 15.5.0 中被弃用,并将在 v19 版本的 React 包中完全移除。如果您正在使用 propTypes
,建议迁移到 TypeScript 或其他类型检查解决方案。
您可能一直在使用 propTypes
来验证传递给函数组件的道具,以确保它们接收到正确的类型和值。例如:
import PropTypes from 'prop-types'; function CustomButton({ label, color }) { return <button style={{ backgroundColor: color }}>{ label }</button>; } CustomButton.defaultProps = { label: 'Click me', color: 'blue', }; CustomButton.propTypes = { label: PropTypes.string, color: PropTypes.string, };
如今,您可以开始使用 TypeScript 进行这些类型检查:
type CustomButtonProps = { label?: string; color?: string; }; const CustomButton = ({ label = 'Click me', color = 'blue' }: CustomButtonProps) => { return <button style={{ backgroundColor: color }}>{ label }</button>; };
注:为了帮助您从使用 propTypes
切换到 TypeScript,您可以使用以下 codemod 命令:
npx codemod@latest react/prop-types-typescript
3. 移除传统上下文(contextTypes 和 getChildContext)
鉴于 WordPress 中许多插件和代码库的长期存在,您可能仍在您的类组件中使用传统的 contextTypes
和 getChildContext
API。这些应用程序接口用于将数据从父组件传递到其子组件,而无需在每个层级明确传递道具。
不过,值得注意的是,传统上下文已在 React 16.6.0 中被弃用,并将在 React v19 中移除。这一改动的目的是让 React 稍微更小更快,因为传统 Context API 有一些细微的 bug,经常容易被忽视。
传统方法已被新的 contextType
API 所取代。
下面是一个示例,说明您如何在 WordPress 插件中使用已废弃的 Context API,将全局设置(如网站标题)从父组件传递到子组件,而无需进行道具钻取:
import PropTypes from 'prop-types'; class SettingsProvider extends React.Component { static childContextTypes = { siteTitle: PropTypes.string.isRequired, }; getChildContext() { return { siteTitle: 'My WordPress Site' }; } render() { return <SettingsConsumer />; } } class SettingsConsumer extends React.Component { static contextTypes = { siteTitle: PropTypes.string.isRequired, }; render() { return <div>Site Title: {this.context.siteTitle}</div>; } }
相比之下,现代方法使用 createContext
方法。在准备 React 19 时,您应该采用这种方法:
import React from 'react'; const SettingsContext = React.createContext(); class SettingsProvider extends React.Component { render() { return ( <SettingsContext value={{ siteTitle: 'My WordPress Site' }}> <SettingsConsumer /> </SettingsContext> ); } } class SettingsConsumer extends React.Component { static contextType = SettingsContext; render() { const { siteTitle } = this.context; return <div>Site Title: { siteTitle }</div>; } }
4. 移除字符串引用
使用字符串引用曾是在 React 组件中访问 DOM 元素的常用方法。不过,自 React 16.3.0 以来,这种方法一直被视为传统方法,并将在 v19 中移除。
字符串引用虽然简单明了,但也存在一些问题,例如潜在的命名冲突和缺乏灵活性。
请看一个在 WordPress 自定义块中使用字符串引用的例子。想象一下,您有一个包含输入框的 Gutenberg 自定义区块,您希望在将该区块添加到编辑器时自动聚焦输入框。下面是您如何使用字符串引用来实现这一目的:
class CustomBlock extends React.Component { componentDidMount() { this.refs.input.focus(); } render() { return <input ref="input" placeholder="Enter text..." />; } }
要为 React 19 做好准备,您必须使用回调 ref
或 React.createRef
API 替换字符串 ref。下面是使用回调 ref
的相同示例:
class CustomBlock extends React.Component { componentDidMount() { this.input.focus(); } render() { return <input ref={(input) => (this.input = input)} placeholder="Enter text..." />; } }
注:为了帮助您从使用字符串 ref 切换到回调 ref
,您可以使用以下 codemod 命令:
npx codemod@latest react/19/replace-string-ref
5. 移除模块模式工厂
React 19 将移除的另一个废弃功能是模块模式工厂。这种模式很少被使用,而且会导致 React 略微变大和变慢。
模块模式工厂允许开发人员不那么传统地创建组件。下面是一个您可能正在使用它的示例:
function SettingsPanelFactory() { return { render() { return ( <div className="settings-panel"> <h2>Settings</h2> {/* other settings UI components */} </div> ); } }; }
在此模式中,SettingsPanelFactory
使用 render
方法返回对象,而不是直接返回 JSX。
为了符合 React 19 的要求,您必须将模块模式工厂迁移到直接返回 JSX 的常规函数。以下是更新后的示例:
function SettingsPanel() { return ( <div className="settings-panel"> <h2>Settings</h2> {/* other settings UI components */} </div> ); }
6. 移除 createFactory API
React 19 将移除 React.createFactory
。在JSX得到广泛支持之前,这种方法比较常用。它允许开发人员在不使用 JSX 语法的情况下创建 React 元素。
不过,随着 JSX 的普及,createFactory
已经过时,可以用更简单、更易读的 JSX 代码来替代。
下面是一个使用 createFactory
创建 button
元素的示例。这可能是自定义 WordPress 插件的一部分,该插件可根据用户输入动态生成 button
元素:
import { createFactory } from 'react'; const button = createFactory('button'); function CustomButton() { return button({ className: 'custom-button', type: 'button' }, 'Click Me'); }
要为 React 19 更新此代码,请将 createFactory
替换为 JSX。这一改动使代码更现代、更易读、更易维护:
function CustomButton() { return <button className="custom-button" type="button">Click Me</button>; }
7. 移除 react-test-renderer/shallow
React 19 删除了 react-test-renderer/shallow
,以简化测试实用程序并鼓励最佳实践。在 React 18 中,react-test-renderer/shallow
已更新为重新导出 react-shallow-renderer。
以前,您可能会使用 react-test-renderer/shallow
为 React 组件创建浅层呈现测试:
import ShallowRenderer from 'react-test-renderer/shallow'; test('MyComponent shallow render', () => { const renderer = new ShallowRenderer(); renderer.render(<MyComponent />); const result = renderer.getRenderOutput(); expect(result.type).toBe('div'); });
要符合 React 19 的要求,需要安装 react-shallow-renderer:
npm install react-shallow-renderer --save-dev
并更新您的导入:
import ShallowRenderer from 'react-shallow-renderer'; test('MyComponent shallow render', () => { const renderer = new ShallowRenderer(); renderer.render(<MyComponent />); const result = renderer.getRenderOutput(); expect(result.type).toBe('div'); });
React 团队建议迁移到 React 测试库,该库通过关注用户如何与组件交互来提供更强大的测试实践。
为此,请将 @testing-library/react 库作为开发依赖安装:
npm install @testing-library/react --save-dev
接下来,您可以使用这种现代方法测试同一个组件:
import { render, screen } from '@testing-library/react'; import MyBlock from './MyBlock'; test('MyBlock renders correctly', () => { render(<MyBlock />); const element = screen.getByText('MyBlock content'); expect(element).toBeInTheDocument(); });
删除 React DOM 中的弃用方法
React DOM 在 React 19 中也发生了变化,删除了某些已废弃的方法。本节将概述这些更改,并指导您更新与 DOM 相关的代码。
1. 删除 react-dom/test-utils API
react-dom/test-utils API 也将在 React 19 中移除。这将影响我们为 React 组件编写测试的方式。具体来说,act
工具已从 react-dom/test-utils
移至 react
包。
此外,react-dom/test-utils
中的大多数其他实用工具也已移除。下面介绍如何调整测试以适应这些变化。
act
实用程序对于确保与测试相关的所有更新都已处理并应用到 DOM 至关重要。在 React 19 中,您应该直接从 react
中导入 act
,而不是 react-dom/test-utils
。
// Before import { act } from 'react-dom/test-utils'; // Now import { act } from 'react';
注:为了帮助你从使用 react-dom/test-utils
切换到新的导入,你可以使用下面的 codemod 命令:
npx codemod@latest react/19/replace-act-import
React 团队还建议将您的测试迁移到 React 测试库,以获得现代化且支持良好的测试体验。以下是一些常见用例以及如何更新它们。
renderIntoDocument
实用程序将被移除。您可以使用 @testing-library/react
中的 render
来替换它。
// Before import { renderIntoDocument } from 'react-dom/test-utils'; renderIntoDocument(<Component />); // Now import { render } from '@testing-library/react'; render(<Component />);
同样,用于模拟事件的 Simulate
工具也将被移除。取而代之的是使用 @testing-library/react
中的 fireEvent
,它会在元素上派发实际事件。
// Before import { Simulate } from 'react-dom/test-utils'; const element = document.querySelector('button'); Simulate.click(element); // Now import { fireEvent } from '@testing-library/react'; const element = document.querySelector('button'); fireEvent.click(element);
请注意,fireEvent
派发的是真实事件,这意味着它与元素的交互比模拟
创建的合成事件更自然。要正确理解 React 测试库,请阅读其文档。
2. 删除 findDOMNode API
React 19 的另一个重大变化是移除了 ReactDOM .findDOMNode
,该函数在 React 16.6.0 中已被弃用。
该函数用于访问 React 组件的底层 DOM 节点,但它有几个缺点,例如执行速度慢、易受重构影响和破坏抽象层。
相反,您应该使用 DOM refs,它提供了一种更可靠、更高效的方式来与 React 组件中的 DOM 元素进行交互。
下面是使用 findDOMNode
在组件挂载时选择输入字段中文本的示例:
import { findDOMNode } from 'react-dom'; function AutoselectingInput() { useEffect(() => { const input = findDOMNode(this); input.select() }, []); render() { return <input defaultValue="Hello" />; } }
要为 React 19 更新此代码,请将 findDOMNode
替换为 ref
。这一改动使代码更加健壮,并与现代 React 实践保持一致:
import React, { useEffect, useRef } from 'react'; function AutoselectingInput() { const inputRef = useRef(null); useEffect(() => { inputRef.current.select(); }, []); return <input ref={inputRef} defaultValue="Hello" />; }
3. 移除渲染 API
React 19 将移除 ReactDOM.
render。该方法已在 React 18.0.0 中被弃用,取而代之的是 react-dom/client
中的 createRoot
API,它为初始化和渲染 React 应用程序提供了一种更高效、更现代的方式。这一变化是 React 为简化和优化库而持续努力的一部分。
在典型的 WordPress 设置中,您可能会有一个自定义块或插件,在 DOM 准备就绪时初始化 React 应用程序。以前,您会使用 ReactDOM.render
:
import { render } from 'react-dom'; render(<App />, document.getElementById('root'));
在 React 19 中,您应该使用 createRoot
来初始化和呈现您的 React 应用程序:
import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')); root.render(<App />);
注:为了帮助您从使用 ReactDOM.render
切换到 react-dom/client
的 createRoot
API,您可以使用以下 codemod 命令:
npx codemod@latest react/19/replace-reactdom-render
4. 删除 unmountComponentAtNode API
React 19 还移除了 ReactDOM.unmountComponentAtNode
方法,该方法在 React 18.0.0 中已被弃用。
在 React 19 中,您应该迁移到使用 root.unmount()
方法,该方法更符合用于创建和水化根的更新 API。
// Before unmountComponentAtNode(document.getElementById('root')); // Now root.unmount();
注:要从使用 unmountComponentAtNode
切换到 root.unmount
,可以使用以下 codemod 命令:
npx codemod@latest react/19/replace-reactdom-render
5. 移除 hydrate API
ReactDOM.hydrate 在 React18 中被弃用,并将在 React 19 中完全移除。
React DOM 客户端 API 的新方法 hydrateRoot
将取代 ReactDOM.hydrate
,为服务器渲染的 React 应用程序提供更高效、更现代的水合方式。
在 WordPress 环境中,您可能会使用服务器端渲染(SSR)来交付初始 HTML 内容,以加快页面加载速度。要将这些内容水合到交互式 React 应用程序中,您以前需要使用 ReactDOM.hydrate
:
import { hydrate } from 'react-dom'; import App from './App.js'; hydrate( <App />, document.getElementById('root') );
使用 React 19 时,应使用react-dom/client
中的hydrateRoot
进行 hydration:
import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot( document.getElementById('root'), <App /> );
注:To help you switch from ReactDOM.hydrate
to ReactDOMClient.hydrateRoot
, you can use the following codemod command:
npx codemod@latest react/19/replace-reactdom-render
删除已废弃的 TypeScript 类型
WordPress 开发人员经常使用 TypeScript 为 React 组件添加类型安全性并提高代码质量。在 React 19 中,一些已被弃用的 TypeScript 类型已被移除或迁移到更相关的包中。
了解这些变化对于确保您的代码库保持稳健并与最新的 React 版本兼容至关重要。
为了协助过渡,React 团队提供了一个名为 types-react-codemod 的工具,它可以自动更新您的代码库以处理这些变更。
要使用该工具,请运行下面的 codemod 命令,其中包含几个用于更新已废弃类型的转换。
npx types-react-codemod@latest preset-19 ./path-to-app
该工具还提供互动模式,您可以选择要应用的特定变换:
? Pick transforms to apply (Press to select, to toggle all, to invert selection, and to proceed) ❯◯ context-any ◉ deprecated-react-type ◉ deprecated-sfc-element ◉ deprecated-sfc ◉ deprecated-stateless-component ◯ implicit-children ◯ useCallback-implicit-any
让我们举例说明一些关键的变化。
1. 需要 ref 清理
在 React 19 中,ref
清理函数通过在 ref
回调中强制执行显式返回来提高类型安全性。隐式返回会导致 TypeScript 误解返回值。
// Before (instance = current)} /> // Now { instance = current }} />
2. useRef 需要一个参数
以前,useRef
可以在没有参数的情况下调用,从而导致潜在的类型问题。在 React 19 中,useRef
需要一个参数,以确保 ref 始终是可变的。
// Before — @ts-expect-error: Expected 1 argument but saw none useRef(); // Now — correct usage with an argument useRef(undefined);
3. 对 ReactElement TypeScript 类型的更改
ReactElement
props 的默认类型已从 any
变为 unknown
,通过要求显式处理未知类型,提高了类型安全性。
// Previously, this was 'any' type Example = ReactElement["props"]; // Now, this is 'unknown' type Example = ReactElement["props"];
如果您的代码依赖于any
类型,则必须更新代码以显式处理 unknown
类型或将其转换为 any
类型。
小结
作为 WordPress 开发人员,了解 React 的最新进展至关重要。本指南将确保您了解 React 即将发生的各种变化,以便将它们应用到您的 WordPress 项目中。
最后一条信息: React 19 将要求使用新的 JSX transform。好消息是,WordPress 6.6 已经配备了它。如果未启用新 transform,您将看到以下警告:
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
您所要做的就是停止使用 React 导入进行 JSX 转换,因为它们已不再必要。