Component
Component 是作为 JavaScript 类 定义的 React 组件的基类。React 仍然支持类组件,但我们不推荐在新代码中使用它们。
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}- 参考
Componentcontextpropsstateconstructor(props)componentDidCatch(error, info)componentDidMount()componentDidUpdate(prevProps, prevState, snapshot?)componentWillMount()componentWillReceiveProps(nextProps)componentWillUpdate(nextProps, nextState)componentWillUnmount()forceUpdate(callback?)getSnapshotBeforeUpdate(prevProps, prevState)render()setState(nextState, callback?)shouldComponentUpdate(nextProps, nextState, nextContext)UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps(nextProps, nextContext)UNSAFE_componentWillUpdate(nextProps, nextState)static contextTypestatic defaultPropsstatic getDerivedStateFromError(error)static getDerivedStateFromProps(props, state)
- 用法
- 备选方案
参考
🌐 Reference
Component
要将 React 组件定义为类,请扩展内置的 Component 类并定义一个 render 方法:
🌐 To define a React component as a class, extend the built-in Component class and define a render method:
import { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}只有 render 方法是必需的,其他方法是可选的。
🌐 Only the render method is required, other methods are optional.
context
类组件的 context 可通过 this.context 获取。只有在使用 static contextType 指定你想要接收的 context 时,它才可用。
🌐 The context of a class component is available as this.context. It is only available if you specify which context you want to receive using static contextType.
类组件一次只能读取一个上下文。
🌐 A class component can only read one context at a time.
class Button extends Component {
static contextType = ThemeContext;
render() {
const theme = this.context;
const className = 'button-' + theme;
return (
<button className={className}>
{this.props.children}
</button>
);
}
}props
传递给类组件的 props 可以通过 this.props 访问。
🌐 The props passed to a class component are available as this.props.
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
<Greeting name="Taylor" />state
类组件的状态可以通过 this.state 获取。state 字段必须是一个对象。不要直接修改状态。如果你希望更改状态,请使用新的状态调用 setState。
🌐 The state of a class component is available as this.state. The state field must be an object. Do not mutate the state directly. If you wish to change the state, call setState with the new state.
class Counter extends Component {
state = {
age: 42,
};
handleAgeChange = () => {
this.setState({
age: this.state.age + 1
});
};
render() {
return (
<>
<button onClick={this.handleAgeChange}>
Increment age
</button>
<p>You are {this.state.age}.</p>
</>
);
}
}constructor(props)
构造函数 会在你的类组件 挂载(被添加到屏幕上)之前运行。通常,构造函数在 React 中只有两个用途。它允许你声明状态,并将你的类方法 绑定 到类实例:
🌐 The constructor runs before your class component mounts (gets added to the screen). Typically, a constructor is only used for two purposes in React. It lets you declare state and bind your class methods to the class instance:
class Counter extends Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// ...
}如果你使用现代 JavaScript 语法,构造函数很少需要。相反,你可以使用 公共类字段语法 重写上面的代码,这种语法被现代浏览器和像 Babel: 这样的工具所支持。
🌐 If you use modern JavaScript syntax, constructors are rarely needed. Instead, you can rewrite this code above using the public class field syntax which is supported both by modern browsers and tools like Babel:
class Counter extends Component {
state = { counter: 0 };
handleClick = () => {
// ...
}构造函数不应包含任何副作用或订阅。
🌐 A constructor should not contain any side effects or subscriptions.
参数
🌐 Parameters
props:组件的初始属性。
返回
🌐 Returns
constructor 不应返回任何内容。
注意事项
🌐 Caveats
- 不要在构造函数中运行任何副作用或订阅。相反,应使用
componentDidMount来实现。 - 在构造函数内部,你需要在任何其他语句之前调用
super(props)。如果你不这样做,构造函数运行时this.props将是undefined,这可能会令人困惑并导致错误。 - 构造函数是唯一可以直接赋值
this.state的地方。在所有其他方法中,你需要使用this.setState()。不要在构造函数中调用setState。 - 当你使用服务器渲染, 构造函数也会在服务器上运行,随后是
render方法。然而,像componentDidMount或componentWillUnmount这样的生命周期方法不会在服务器上运行。 - 当 严格模式 开启时,React 会在开发环境中调用
constructor两次,然后丢弃其中一个实例。这有助于你注意到需要从constructor中移出的意外副作用。
componentDidCatch(error, info)
如果你定义了 componentDidCatch,当某个子组件(包括远程子组件)在渲染过程中抛出错误时,React 会调用它。这可以让你在生产环境中将该错误记录到错误报告服务中。
🌐 If you define componentDidCatch, React will call it when some child component (including distant children) throws an error during rendering. This lets you log that error to an error reporting service in production.
通常,它与 static getDerivedStateFromError 一起使用,后者允许你在发生错误时更新状态并向用户显示错误消息。具有这些方法的组件被称为错误边界。
🌐 Typically, it is used together with static getDerivedStateFromError which lets you update state in response to an error and display an error message to the user. A component with these methods is called an Error Boundary.
参数
🌐 Parameters
error:抛出的错误。在实际操作中,它通常是Error的一个实例,但这并不保证,因为 JavaScript 允许throw任何值,包括字符串甚至null。info:包含有关错误的附加信息的对象。其componentStack字段包含一个堆栈跟踪,其中包括引发错误的组件,以及所有父组件的名称和源位置。在生产环境中,组件名称将被压缩。如果你设置了生产错误报告,你可以使用 source map 解码组件堆栈,就像处理常规 JavaScript 错误堆栈一样。
返回
🌐 Returns
componentDidCatch 不应返回任何内容。
注意事项
🌐 Caveats
- 过去,通常会在
componentDidCatch内调用setState来更新 UI 并显示备用错误消息。这种做法已被弃用,建议改为定义static getDerivedStateFromError - React 的生产版和开发版在
componentDidCatch处理错误的方式上略有不同。在开发环境中,错误会冒泡到window,这意味着任何window.onerror或window.addEventListener('error', callback)都会拦截已被componentDidCatch捕获的错误。而在生产环境中,错误则不会冒泡,这意味着任何祖级错误处理程序只会接收到未被componentDidCatch明确捕获的错误。
componentDidMount()
如果你定义了 componentDidMount 方法,当你的组件被添加(挂载)到屏幕上时,React 会调用它。这是开始数据获取、设置订阅或操作 DOM 节点的常见位置。
🌐 If you define the componentDidMount method, React will call it when your component is added (mounted) to the screen. This is a common place to start data fetching, set up subscriptions, or manipulate the DOM nodes.
如果你实现 componentDidMount,通常需要实现其他生命周期方法以避免错误。例如,如果 componentDidMount 读取某些状态或属性,你还必须实现 componentDidUpdate 来处理它们的变化,以及 componentWillUnmount 来清理 componentDidMount 所做的工作。
🌐 If you implement componentDidMount, you usually need to implement other lifecycle methods to avoid bugs. For example, if componentDidMount reads some state or props, you also have to implement componentDidUpdate to handle their changes, and componentWillUnmount to clean up whatever componentDidMount was doing.
class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};
componentDidMount() {
this.setupConnection();
}
componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}
componentWillUnmount() {
this.destroyConnection();
}
// ...
}参数
🌐 Parameters
componentDidMount不接受任何参数。
返回
🌐 Returns
componentDidMount 不应返回任何内容。
注意事项
🌐 Caveats
- 当开启 严格模式 时,在开发环境中,React 会先调用
componentDidMount,然后立即调用componentWillUnmount,最后再次调用componentDidMount。这可以帮助你注意到是否忘记实现componentWillUnmount,或者它的逻辑是否没有完全“镜像”componentDidMount所做的事情。 - 尽管你可以在
componentDidMount中立即调用setState,但最好在可能的情况下避免这样做。它会触发额外的渲染,但会发生在浏览器更新屏幕之前。这保证了即使在这种情况下render会被调用两次,用户也不会看到中间状态。谨慎使用这种模式,因为它经常导致性能问题。在大多数情况下,你应该能够在constructor中分配初始状态。不过,在一些情况下,这可能是必要的,比如当你需要在渲染依赖于 DOM 节点大小或位置的内容之前测量该节点时,像模态框和工具提示就是这种情况。
componentDidUpdate(prevProps, prevState, snapshot?)
如果你定义了 componentDidUpdate 方法,React 会在组件使用更新后的 props 或 state 重新渲染后立即调用它。这个方法在初次渲染时不会被调用。
🌐 If you define the componentDidUpdate method, React will call it immediately after your component has been re-rendered with updated props or state. This method is not called for the initial render.
你可以在更新后使用它来操作 DOM。只要将当前 props 与之前的 props 进行比较,这也是进行网络请求的常见位置(例如,如果 props 没有变化,可能不需要进行网络请求)。通常,你会将它与 componentDidMount 和 componentWillUnmount: 一起使用。
🌐 You can use it to manipulate the DOM after an update. This is also a common place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed). Typically, you’d use it together with componentDidMount and componentWillUnmount:
class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};
componentDidMount() {
this.setupConnection();
}
componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}
componentWillUnmount() {
this.destroyConnection();
}
// ...
}参数
🌐 Parameters
prevProps:更新前的属性。比较prevProps和this.props以确定发生了什么变化。prevState:更新前的状态。将prevState与this.state进行比较以确定有什么变化。snapshot:如果你实现了getSnapshotBeforeUpdate,snapshot将包含你从该方法返回的值。否则,它将是undefined。
返回
🌐 Returns
componentDidUpdate 不应返回任何内容。
注意事项
🌐 Caveats
componentDidUpdate如果shouldComponentUpdate被定义并返回false,将不会被调用。componentDidUpdate内的逻辑通常应封装在比较this.props与prevProps以及this.state与prevState的条件中。否则,会有产生无限循环的风险。- 虽然你可以在
componentDidUpdate中立即调用setState,但最好在可能的情况下避免这样做。这样会触发额外的渲染,但会在浏览器更新屏幕之前发生。这保证了即使在这种情况下render会被调用两次,用户也不会看到中间状态。这种模式经常会引起性能问题,但在诸如模态框和工具提示等罕见情况下可能是必要的,当你需要在渲染依赖其大小或位置的内容之前测量 DOM 节点时。
componentWillMount()
componentWillReceiveProps(nextProps)
componentWillUpdate(nextProps, nextState)
componentWillUnmount()
如果你定义了 componentWillUnmount 方法,React 会在你的组件从屏幕上被移除(卸载)之前调用它。这是取消数据获取或移除订阅的常见位置。
🌐 If you define the componentWillUnmount method, React will call it before your component is removed (unmounted) from the screen. This is a common place to cancel data fetching or remove subscriptions.
componentWillUnmount 内部的逻辑应该“镜像”componentDidMount 内部的逻辑。例如,如果 componentDidMount 设置了一个订阅,componentWillUnmount 应该清理该订阅。如果你在 componentWillUnmount 中的清理逻辑读取了一些 props 或 state,通常你还需要实现 componentDidUpdate 来清理对应于旧的 props 和 state 的资源(例如订阅)。
🌐 The logic inside componentWillUnmount should “mirror” the logic inside componentDidMount. For example, if componentDidMount sets up a subscription, componentWillUnmount should clean up that subscription. If the cleanup logic in your componentWillUnmount reads some props or state, you will usually also need to implement componentDidUpdate to clean up resources (such as subscriptions) corresponding to the old props and state.
class ChatRoom extends Component {
state = {
serverUrl: 'https://localhost:1234'
};
componentDidMount() {
this.setupConnection();
}
componentDidUpdate(prevProps, prevState) {
if (
this.props.roomId !== prevProps.roomId ||
this.state.serverUrl !== prevState.serverUrl
) {
this.destroyConnection();
this.setupConnection();
}
}
componentWillUnmount() {
this.destroyConnection();
}
// ...
}参数
🌐 Parameters
componentWillUnmount 不接受任何参数。
返回
🌐 Returns
componentWillUnmount 不应返回任何内容。
注意事项
🌐 Caveats
- 当开启 严格模式 时,在开发环境中,React 会先调用
componentDidMount,然后立即调用componentWillUnmount,之后再次调用componentDidMount。这可以帮助你注意到是否忘记实现componentWillUnmount,或者它的逻辑是否没有完全“镜像”componentDidMount所做的操作。
forceUpdate(callback?)
强制组件重新渲染。
🌐 Forces a component to re-render.
通常,这不是必要的。如果你的组件的 render 方法只从 this.props、this.state 或 this.context 读取数据,当你在组件或其父组件中调用 setState 时,它会自动重新渲染。然而,如果你的组件的 render 方法直接从外部数据源读取数据,你必须告诉 React 在该数据源发生变化时更新用户界面。这正是 forceUpdate 所能够做到的。
🌐 Usually, this is not necessary. If your component’s render method only reads from this.props, this.state, or this.context, it will re-render automatically when you call setState inside your component or one of its parents. However, if your component’s render method reads directly from an external data source, you have to tell React to update the user interface when that data source changes. That’s what forceUpdate lets you do.
尽量避免所有对 forceUpdate 的使用,只在 render 中从 this.props 和 this.state 读取。
🌐 Try to avoid all uses of forceUpdate and only read from this.props and this.state in render.
参数
🌐 Parameters
- 可选
callback如果指定,React 会在更新提交后调用你提供的callback。
返回
🌐 Returns
forceUpdate 不返回任何值。
注意事项
🌐 Caveats
- 如果你调用
forceUpdate,React 会重新渲染,而不会调用shouldComponentUpdate.
getSnapshotBeforeUpdate(prevProps, prevState)
如果你实现了 getSnapshotBeforeUpdate,React 会在更新 DOM 之前立即调用它。它使你的组件能够在 DOM 可能被更改之前捕获一些信息(例如滚动位置)。此生命周期方法返回的任何值将作为参数传递给 componentDidUpdate.
🌐 If you implement getSnapshotBeforeUpdate, React will call it immediately before React updates the DOM. It enables your component to capture some information from the DOM (e.g. scroll position) before it is potentially changed. Any value returned by this lifecycle method will be passed as a parameter to componentDidUpdate.
例如,你可以在聊天线程等需要在更新期间保持滚动位置的 UI 中使用它:
🌐 For example, you can use it in a UI like a chat thread that needs to preserve its scroll position during updates:
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}></div>
);
}
}在上面的例子中,重要的是要直接在 getSnapshotBeforeUpdate 中读取 scrollHeight 属性。在 render、UNSAFE_componentWillReceiveProps 或 UNSAFE_componentWillUpdate 中读取它是不安全的,因为这些方法被调用和 React 更新 DOM 之间可能存在时间差。
🌐 In the above example, it is important to read the scrollHeight property directly in getSnapshotBeforeUpdate. It is not safe to read it in render, UNSAFE_componentWillReceiveProps, or UNSAFE_componentWillUpdate because there is a potential time gap between these methods getting called and React updating the DOM.
参数
🌐 Parameters
prevProps:更新前的属性。比较prevProps和this.props以确定发生了什么变化。prevState:更新前的状态。将prevState与this.state进行比较以确定有什么变化。
返回
🌐 Returns
你应该返回任意类型的快照值,或者 null。你返回的值将作为第三个参数传递给 componentDidUpdate.
🌐 You should return a snapshot value of any type that you’d like, or null. The value you returned will be passed as the third argument to componentDidUpdate.
注意事项
🌐 Caveats
getSnapshotBeforeUpdate如果shouldComponentUpdate被定义并返回false,将不会被调用。
render()
render 方法是类组件中唯一必需的方法。
🌐 The render method is the only required method in a class component.
render 方法应指定你希望在屏幕上显示的内容,例如:
🌐 The render method should specify what you want to appear on the screen, for example:
import { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}React 可能在任何时刻调用 render,所以你不应该假设它会在特定时间运行。通常,render 方法应该返回一段 JSX,但也支持一些 其他返回类型(比如字符串)。为了计算返回的 JSX,render 方法可以读取 this.props、this.state 和 this.context。
🌐 React may call render at any moment, so you shouldn’t assume that it runs at a particular time. Usually, the render method should return a piece of JSX, but a few other return types (like strings) are supported. To calculate the returned JSX, the render method can read this.props, this.state, and this.context.
你应该将 render 方法写成一个纯函数,这意味着如果 props、state 和 context 相同,它应该返回相同的结果。它也不应该包含副作用(比如设置订阅)或与浏览器 API 交互。副作用应该发生在事件处理器中或像 componentDidMount. 这样的方法中。
🌐 You should write the render method as a pure function, meaning that it should return the same result if props, state, and context are the same. It also shouldn’t contain side effects (like setting up subscriptions) or interact with the browser APIs. Side effects should happen either in event handlers or methods like componentDidMount.
参数
🌐 Parameters
render不接受任何参数。
返回
🌐 Returns
render 可以返回任何有效的 React 节点。这包括 React 元素,如 <div />,字符串、数字、portals、空节点(null、undefined、true 和 false)以及 React 节点数组。
注意事项
🌐 Caveats
render应该被写成 props、state 和 context 的纯函数。它不应该有副作用。render如果shouldComponentUpdate被定义并返回false,将不会被调用。- 当启用 严格模式 时,React 会在开发环境中调用
render两次,然后丢弃其中一个结果。这有助于你注意到需要从render方法中移出的意外副作用。 render调用与随后的componentDidMount或componentDidUpdate调用之间没有一一对应关系。当有利时,React 可能会丢弃部分render调用的结果。
setState(nextState, callback?)
调用 setState 来更新你的 React 组件的状态。
🌐 Call setState to update the state of your React component.
class Form extends Component {
state = {
name: 'Taylor',
};
handleNameChange = (e) => {
const newName = e.target.value;
this.setState({
name: newName
});
}
render() {
return (
<>
<input value={this.state.name} onChange={this.handleNameChange} />
<p>Hello, {this.state.name}.</p>
</>
);
}
}setState 将更改加入组件状态队列。它告诉 React,这个组件及其子组件需要使用新的状态重新渲染。这是你响应交互更新用户界面的主要方式。
你也可以向 setState 传递一个函数。它允许你基于之前的状态来更新状态:
🌐 You can also pass a function to setState. It lets you update state based on the previous state:
handleIncreaseAge = () => {
this.setState(prevState => {
return {
age: prevState.age + 1
};
});
}你不必这样做,但如果你想在同一事件期间多次更新状态,这样做会很方便。
🌐 You don’t have to do this, but it’s handy if you want to update state multiple times during the same event.
参数
🌐 Parameters
nextState:对象或函数。- 如果你将一个对象作为
nextState传入,它将被浅层合并到this.state中。 - 如果你将一个函数作为
nextState传入,它将被视为一个 更新函数。它必须是纯函数,应该以待处理的 state 和 props 作为参数,并且应该返回一个对象,该对象将被浅合并到this.state中。React 会将你的更新函数放入队列中并重新渲染你的组件。在下一次渲染期间,React 会通过将所有排队的更新函数应用于前一个 state 来计算下一个 state。
- 如果你将一个对象作为
- 可选
callback:如果指定,React将在更新提交后调用你提供的callback。
返回
🌐 Returns
setState 不返回任何值。
注意事项
🌐 Caveats
- 将
setState看作是一个 请求,而不是立即更新组件的命令。当多个组件响应同一事件更新它们的状态时,React 会将它们的更新批量处理,并在事件结束时一次性重新渲染它们。在极少数情况下,如果你需要强制某个状态更新同步应用,你可以将其封装在flushSync中,但这可能会影响性能。 setState不会立即更新this.state。这会导致在调用setState后立即读取this.state成为潜在陷阱。相反,请使用componentDidUpdate或 setState 的callback参数,这两者都能保证在更新应用后触发。如果你需要基于之前的状态来设置状态,可以像上面描述的那样将一个函数传递给nextState。
shouldComponentUpdate(nextProps, nextState, nextContext)
如果你定义了 shouldComponentUpdate,React 将调用它来确定是否可以跳过重新渲染。
🌐 If you define shouldComponentUpdate, React will call it to determine whether a re-render can be skipped.
如果你有信心想手写它,你可以比较 this.props 与 nextProps,以及 this.state 与 nextState,并返回 false 来告诉 React 可以跳过更新。
🌐 If you are confident you want to write it by hand, you may compare this.props with nextProps and this.state with nextState and return false to tell React the update can be skipped.
class Rectangle extends Component {
state = {
isHovered: false
};
shouldComponentUpdate(nextProps, nextState) {
if (
nextProps.position.x === this.props.position.x &&
nextProps.position.y === this.props.position.y &&
nextProps.size.width === this.props.size.width &&
nextProps.size.height === this.props.size.height &&
nextState.isHovered === this.state.isHovered
) {
// Nothing has changed, so a re-render is unnecessary
return false;
}
return true;
}
// ...
}当接收到新的 props 或 state 时,React 会在渲染之前调用 shouldComponentUpdate。默认值为 true。在初始渲染或使用 forceUpdate 时不会调用此方法。
🌐 React calls shouldComponentUpdate before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate is used.
参数
🌐 Parameters
nextProps:组件即将渲染的下一个 props。比较nextProps与this.props以确定发生了什么变化。nextState:组件即将渲染的下一个状态。比较nextState与this.state以确定发生了什么变化。nextContext:组件即将渲染的下一个上下文。将nextContext与this.context进行比较以确定发生了什么变化。仅在你指定static contextType时可用。
返回
🌐 Returns
如果你希望组件重新渲染,请返回 true。这是默认行为。
🌐 Return true if you want the component to re-render. That’s the default behavior.
返回 false 以告诉 React 可以跳过重新渲染。
🌐 Return false to tell React that re-rendering can be skipped.
注意事项
🌐 Caveats
- 这个方法仅仅作为性能优化而存在。如果你的组件在没有它的情况下出问题,请先修复这一点。
- 考虑使用
PureComponent替代手写shouldComponentUpdate。PureComponent会浅比较 props 和 state,从而减少跳过必要更新的可能性。 - 我们不推荐在
shouldComponentUpdate中进行深度相等检查或使用JSON.stringify。它会使性能不可预测,并依赖于每个属性和状态的数据结构。在最好的情况下,你可能会给应用引入数秒的停顿,在最坏的情况下,你可能会导致应用崩溃。 - 返回
false并不能阻止子组件在它们的状态变化时重新渲染。 - 返回
false并不能保证组件不会重新渲染。React 会将返回值作为一个提示,但如果出于其他原因有必要,仍可能选择重新渲染你的组件。
UNSAFE_componentWillMount()
如果你定义了 UNSAFE_componentWillMount,React 会在 constructor. 之后立即调用它。它的存在仅出于历史原因,不应在任何新代码中使用。相反,请使用以下替代方案之一:
🌐 If you define UNSAFE_componentWillMount, React will call it immediately after the constructor. It only exists for historical reasons and should not be used in any new code. Instead, use one of the alternatives:
- 要初始化状态,将
state声明为类字段,或在constructor. 内设置this.state - 如果你需要运行副作用或设置订阅,请将该逻辑移到
componentDidMount中。
参数
🌐 Parameters
UNSAFE_componentWillMount不接受任何参数。
返回
🌐 Returns
UNSAFE_componentWillMount 不应返回任何内容。
注意事项
🌐 Caveats
- 如果组件实现了
static getDerivedStateFromProps或getSnapshotBeforeUpdate,UNSAFE_componentWillMount将不会被调用。 - 尽管名称如此,
UNSAFE_componentWillMount并不能保证如果你的应用使用了现代 React 特性(例如Suspense.)组件 一定 会被挂载。如果渲染尝试被挂起(例如,因为某个子组件的代码尚未加载),React 会丢弃正在进行的树,并在下一次尝试时重新从头构建组件。这就是该方法被认为“不安全”的原因。依赖挂载的代码(例如添加订阅)应该放入componentDidMount. UNSAFE_componentWillMount是唯一在 服务器渲染 期间运行的生命周期方法。 从实际用途来看,它与constructor完全相同,因此你应该使用constructor来处理这种类型的逻辑。
UNSAFE_componentWillReceiveProps(nextProps, nextContext)
如果你定义了 UNSAFE_componentWillReceiveProps,当组件接收到新的 props 时,React 会调用它。它的存在仅出于历史原因,不应在任何新代码中使用。相反,应使用以下替代方法之一:
🌐 If you define UNSAFE_componentWillReceiveProps, React will call it when the component receives new props. It only exists for historical reasons and should not be used in any new code. Instead, use one of the alternatives:
- 如果你需要执行副作用(例如,获取数据、运行动画或重新初始化订阅)以响应 prop 的变化,请将该逻辑移动到
componentDidUpdate中。 - 如果你需要**仅在某个属性变化时避免重新计算某些数据,**请使用 记忆化辅助工具 替代。
- 如果你需要在一个属性变化时**“重置”某些状态,**可以考虑将组件改为完全受控或使用 key 完全不受控的方式。
- 如果你需要**在属性改变时“调整”某些状态,**请检查是否可以在渲染过程中仅从属性计算出所有必要信息。如果不能,请改用
static getDerivedStateFromProps。
参数
🌐 Parameters
nextProps:组件即将从其父组件接收的下一个 props。将nextProps与this.props进行比较,以确定发生了哪些变化。nextContext:组件即将从最近的提供者接收到的下一个上下文。将nextContext与this.context比较以确定发生了什么变化。仅在你指定static contextType时可用。
返回
🌐 Returns
UNSAFE_componentWillReceiveProps 不应返回任何内容。
注意事项
🌐 Caveats
- 如果组件实现了
static getDerivedStateFromProps或getSnapshotBeforeUpdate,UNSAFE_componentWillReceiveProps将不会被调用。 - 尽管名称为
UNSAFE_componentWillReceiveProps,但如果你的应用使用现代 React 特性(例如Suspense),它并不能保证组件 一定 会接收到那些 props。如果渲染尝试被挂起(例如,因为某些子组件的代码尚未加载),React 会丢弃正在进行的树,并在下一次尝试时重新从头构建组件。到下一次渲染尝试时,props 可能已经不同。这就是为什么这个方法被认为是“危险的”。那些只应在已提交更新时运行的代码(例如重置订阅)应放入componentDidUpdate中。 UNSAFE_componentWillReceiveProps并不意味着该组件接收到的 props 与上一次不同。你需要自己比较nextProps和this.props来检查是否有变化。- 在挂载期间,React 不会用初始 props 调用
UNSAFE_componentWillReceiveProps。只有组件的一些 props 即将被更新时,它才会调用此方法。例如,调用setState通常不会触发同一组件内部的UNSAFE_componentWillReceiveProps。
UNSAFE_componentWillUpdate(nextProps, nextState)
如果你定义了 UNSAFE_componentWillUpdate,React 会在渲染新的 props 或 state 之前调用它。它存在的原因仅仅是历史原因,不应该在任何新代码中使用。相反,使用以下替代方案之一:
🌐 If you define UNSAFE_componentWillUpdate, React will call it before rendering with the new props or state. It only exists for historical reasons and should not be used in any new code. Instead, use one of the alternatives:
- 如果你需要在属性或状态变化时执行副作用(例如获取数据、运行动画或重新初始化订阅),请将该逻辑移到
componentDidUpdate中。 - 如果你需要从 DOM 中读取一些信息(例如,为了保存当前的滚动位置),以便稍后在
componentDidUpdate中使用,请改为在getSnapshotBeforeUpdate内读取它。
参数
🌐 Parameters
nextProps:组件即将渲染的下一个 props。比较nextProps与this.props以确定发生了什么变化。nextState:组件即将渲染的下一个状态。比较nextState与this.state以确定发生了什么变化。
返回
🌐 Returns
UNSAFE_componentWillUpdate 不应返回任何内容。
注意事项
🌐 Caveats
UNSAFE_componentWillUpdate如果shouldComponentUpdate被定义并返回false,将不会被调用。- 如果组件实现了
static getDerivedStateFromProps或getSnapshotBeforeUpdate,UNSAFE_componentWillUpdate将不会被调用。 - 在
componentWillUpdate期间调用setState(或者任何导致调用setState的方法,例如派发 Redux action)是不被支持的。 - 尽管名称如此,
UNSAFE_componentWillUpdate并不保证如果你的应用使用了现代 React 特性(例如Suspense.),组件 一定 会更新。如果渲染尝试被挂起(例如,因为某个子组件的代码尚未加载),React 会丢弃当前正在进行的树,并在下一次尝试时尝试从头构建组件。到下一次渲染尝试时,props 和 state 可能已经不同。这就是该方法“不安全”的原因。那些只应在已提交更新时运行的代码(例如重置订阅)应放入componentDidUpdate. UNSAFE_componentWillUpdate并不意味着组件接收到的 props 或 state 与上次不同。你需要自己比较nextProps和this.props,以及nextState和this.state,以检查是否有变化。- React 在挂载期间不会使用初始 props 和 state 调用
UNSAFE_componentWillUpdate。
static contextType
如果你想从你的类组件中读取 this.context,你必须指定它需要读取哪个上下文。你作为 static contextType 指定的上下文必须是之前由 createContext 创建的值。
🌐 If you want to read this.context from your class component, you must specify which context it needs to read. The context you specify as the static contextType must be a value previously created by createContext.
class Button extends Component {
static contextType = ThemeContext;
render() {
const theme = this.context;
const className = 'button-' + theme;
return (
<button className={className}>
{this.props.children}
</button>
);
}
}static defaultProps
你可以定义 static defaultProps 来设置类的默认属性。它们将用于 undefined 和缺失的属性,但不会用于 null 属性。
🌐 You can define static defaultProps to set the default props for the class. They will be used for undefined and missing props, but not for null props.
例如,下面是如何定义 color 属性应默认为 'blue':
🌐 For example, here is how you define that the color prop should default to 'blue':
class Button extends Component {
static defaultProps = {
color: 'blue'
};
render() {
return <button className={this.props.color}>click me</button>;
}
}如果未提供 color 属性或其为 undefined,则默认将其设置为 'blue':
🌐 If the color prop is not provided or is undefined, it will be set by default to 'blue':
<>
<Button />
<Button color={undefined} />
<Button color={null} />
<Button color="red" />
</>static getDerivedStateFromError(error)
如果你定义了 static getDerivedStateFromError,当子组件(包括较远的子组件)在渲染时抛出错误,React 会调用它。这让你可以显示错误信息,而不是清空用户界面。
🌐 If you define static getDerivedStateFromError, React will call it when a child component (including distant children) throws an error during rendering. This lets you display an error message instead of clearing the UI.
通常,它与 componentDidCatch 一起使用,这让你可以将错误报告发送到某些分析服务。具有这些方法的组件被称为 错误边界。
🌐 Typically, it is used together with componentDidCatch which lets you send the error report to some analytics service. A component with these methods is called an Error Boundary.
参数
🌐 Parameters
返回
🌐 Returns
static getDerivedStateFromError 应该返回状态,指示组件显示错误信息。
注意事项
🌐 Caveats
static getDerivedStateFromError应该是一个纯函数。如果你想执行副作用(例如,调用分析服务),你还需要实现componentDidCatch.
static getDerivedStateFromProps(props, state)
如果你定义了 static getDerivedStateFromProps,React 会在调用 render 之前调用它,无论是初次挂载还是后续更新。它应该返回一个对象来更新状态,或者返回 null 来不更新任何内容。
🌐 If you define static getDerivedStateFromProps, React will call it right before calling render, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
这种方法存在于少见的使用场景,其中状态依赖于随时间变化的属性。例如,当 userID 属性发生变化时,这个 Form 组件会重置 email 状态:
🌐 This method exists for rare use cases where the state depends on changes in props over time. For example, this Form component resets the email state when the userID prop changes:
class Form extends Component {
state = {
email: this.props.defaultEmail,
prevUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevUserID) {
return {
prevUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}请注意,这个模式要求你在状态(如 prevUserID)中保留 prop(如 userID)的先前值。
🌐 Note that this pattern requires you to keep a previous value of the prop (like userID) in state (like prevUserID).
参数
🌐 Parameters
props:组件即将渲染的下一个属性。state:组件即将渲染的下一个 state。
返回
🌐 Returns
static getDerivedStateFromProps 返回一个对象以更新状态,或者 null 什么也不更新。
注意事项
🌐 Caveats
- 此方法在每次渲染时都会触发,无论原因是什么。这与
UNSAFE_componentWillReceiveProps不同,后者仅在父组件导致重新渲染时触发,而不是由于本地setState的结果。 - 此方法无法访问组件实例。如果你愿意,你可以通过在类定义之外提取组件属性和状态的纯函数,在
static getDerivedStateFromProps与其他类方法之间重用一些代码。
用法
🌐 Usage
定义类组件
🌐 Defining a class component
要将 React 组件定义为类,请扩展内置的 Component 类并定义一个 render 方法:
🌐 To define a React component as a class, extend the built-in Component class and define a render method:
import { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}每当 React 需要确定在屏幕上显示什么时,它都会调用你的 render 方法。通常,你会从中返回一些 JSX。你的 render 方法应该是一个 纯函数: 它只应计算 JSX。
🌐 React will call your render method whenever it needs to figure out what to display on the screen. Usually, you will return some JSX from it. Your render method should be a pure function: it should only calculate the JSX.
与函数组件类似,类组件可以从其父组件通过 props 接收信息。然而,读取 props 的语法有所不同。例如,如果父组件渲染 <Greeting name="Taylor" />,那么你可以从this.props读取 name 属性,如 this.props.name:
🌐 Similarly to function components, a class component can receive information by props from its parent component. However, the syntax for reading props is different. For example, if the parent component renders <Greeting name="Taylor" />, then you can read the name prop from this.props, like this.props.name:
import { Component } from 'react'; class Greeting extends Component { render() { return <h1>Hello, {this.props.name}!</h1>; } } export default function App() { return ( <> <Greeting name="Sara" /> <Greeting name="Cahal" /> <Greeting name="Edite" /> </> ); }
请注意,Hooks(以 use 开头的函数,如 useState)在类组件中不被支持。
🌐 Note that Hooks (functions starting with use, like useState) are not supported inside class components.
为类组件添加状态
🌐 Adding state to a class component
要向类添加 state,请将一个对象分配给名为 state 的属性。要更新状态,请调用 this.setState。
🌐 To add state to a class, assign an object to a property called state. To update state, call this.setState.
import { Component } from 'react'; export default class Counter extends Component { state = { name: 'Taylor', age: 42, }; handleNameChange = (e) => { this.setState({ name: e.target.value }); } handleAgeChange = () => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <> <input value={this.state.name} onChange={this.handleNameChange} /> <button onClick={this.handleAgeChange}> Increment age </button> <p>Hello, {this.state.name}. You are {this.state.age}.</p> </> ); } }
为类组件添加生命周期方法
🌐 Adding lifecycle methods to a class component
你可以在类中定义一些特殊方法。
🌐 There are a few special methods you can define on your class.
如果你定义了 componentDidMount 方法,当你的组件被添加到屏幕上(挂载)时,React 会调用它。React 会在你的组件因 props 或 state 变化重新渲染后调用 componentDidUpdate。React 会在你的组件从屏幕上被移除(卸载)后调用 componentWillUnmount。
🌐 If you define the componentDidMount method, React will call it when your component is added (mounted) to the screen. React will call componentDidUpdate after your component re-renders due to changed props or state. React will call componentWillUnmount after your component has been removed (unmounted) from the screen.
如果你实现 componentDidMount,通常需要实现所有三个生命周期以避免错误。例如,如果 componentDidMount 读取了一些状态或属性,你还必须实现 componentDidUpdate 来处理它们的变化,以及 componentWillUnmount 来清理 componentDidMount 所做的任何事情。
🌐 If you implement componentDidMount, you usually need to implement all three lifecycles to avoid bugs. For example, if componentDidMount reads some state or props, you also have to implement componentDidUpdate to handle their changes, and componentWillUnmount to clean up whatever componentDidMount was doing.
例如,这个 ChatRoom 组件会根据 props 和 state 保持聊天连接的同步:
🌐 For example, this ChatRoom component keeps a chat connection synchronized with props and state:
import { Component } from 'react'; import { createConnection } from './chat.js'; export default class ChatRoom extends Component { state = { serverUrl: 'https://localhost:1234' }; componentDidMount() { this.setupConnection(); } componentDidUpdate(prevProps, prevState) { if ( this.props.roomId !== prevProps.roomId || this.state.serverUrl !== prevState.serverUrl ) { this.destroyConnection(); this.setupConnection(); } } componentWillUnmount() { this.destroyConnection(); } setupConnection() { this.connection = createConnection( this.state.serverUrl, this.props.roomId ); this.connection.connect(); } destroyConnection() { this.connection.disconnect(); this.connection = null; } render() { return ( <> <label> Server URL:{' '} <input value={this.state.serverUrl} onChange={e => { this.setState({ serverUrl: e.target.value }); }} /> </label> <h1>Welcome to the {this.props.roomId} room!</h1> </> ); } }
注意,在开发过程中,当 严格模式 开启时,React 会调用 componentDidMount,立即调用 componentWillUnmount,然后再次调用 componentDidMount。这有助于你注意是否忘记实现 componentWillUnmount 或其逻辑是否没有完全“镜像” componentDidMount 的行为。
🌐 Note that in development when Strict Mode is on, React will call componentDidMount, immediately call componentWillUnmount, and then call componentDidMount again. This helps you notice if you forgot to implement componentWillUnmount or if its logic doesn’t fully “mirror” what componentDidMount does.
使用错误边界捕获渲染错误
🌐 Catching rendering errors with an Error Boundary
默认情况下,如果你的应用在渲染过程中抛出错误,React 会从屏幕上移除其 UI。为了防止这种情况,你可以将 UI 的一部分封装在 错误边界 中。错误边界是一种特殊的组件,它允许你显示一些备用 UI 来替代崩溃的部分——例如,显示一条错误消息。
🌐 By default, if your application throws an error during rendering, React will remove its UI from the screen. To prevent this, you can wrap a part of your UI into an Error Boundary. An Error Boundary is a special component that lets you display some fallback UI instead of the part that crashed—for example, an error message.
要实现一个错误边界组件,你需要提供 static getDerivedStateFromError,它允许你在发生错误时更新状态并向用户显示错误信息。你还可以选择实现 componentDidCatch 来添加一些额外的逻辑,例如,将错误记录到分析服务中。
🌐 To implement an Error Boundary component, you need to provide static getDerivedStateFromError which lets you update state in response to an error and display an error message to the user. You can also optionally implement componentDidCatch to add some extra logic, for example, to log the error to an analytics service.
使用 captureOwnerStack 你可以在开发过程中包括所有者堆栈。
🌐 With captureOwnerStack you can include the Owner Stack during development.
import * as React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToMyService(
error,
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
info.componentStack,
// Warning: `captureOwnerStack` is not available in production.
React.captureOwnerStack(),
);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}
return this.props.children;
}
}然后你可以用它来封装组件树的一部分:
🌐 Then you can wrap a part of your component tree with it:
<ErrorBoundary fallback={<p>Something went wrong</p>}>
<Profile />
</ErrorBoundary>如果 Profile 或其子组件抛出错误,ErrorBoundary 将会“捕获”该错误,显示包含你提供的错误信息的备用界面,并向你的错误报告服务发送生产环境错误报告。
🌐 If Profile or its child component throws an error, ErrorBoundary will “catch” that error, display a fallback UI with the error message you’ve provided, and send a production error report to your error reporting service.
你不需要将每个组件都封装在单独的错误边界中。当你考虑错误边界的粒度时,思考在什么地方显示错误信息是合理的。例如,在一个消息应用中,将错误边界放在会话列表周围是合理的。将错误边界放在每条消息周围也是合理的。然而,将边界放在每个头像周围则没有意义。
🌐 You don’t need to wrap every component into a separate Error Boundary. When you think about the granularity of Error Boundaries, consider where it makes sense to display an error message. For example, in a messaging app, it makes sense to place an Error Boundary around the list of conversations. It also makes sense to place one around every individual message. However, it wouldn’t make sense to place a boundary around every avatar.
备选方案
🌐 Alternatives
将简单组件从类迁移到函数
🌐 Migrating a simple component from a class to a function
通常,你会改为将组件定义为函数 define components as functions 。
🌐 Typically, you will define components as functions instead.
例如,假设你正在将这个 Greeting 类组件转换成函数:
🌐 For example, suppose you’re converting this Greeting class component to a function:
import { Component } from 'react'; class Greeting extends Component { render() { return <h1>Hello, {this.props.name}!</h1>; } } export default function App() { return ( <> <Greeting name="Sara" /> <Greeting name="Cahal" /> <Greeting name="Edite" /> </> ); }
定义一个名为 Greeting 的函数。在这里,你将移动 render 函数的主体。
🌐 Define a function called Greeting. This is where you will move the body of your render function.
function Greeting() {
// ... move the code from the render method here ...
}不要使用 this.props.name,请使用 解构语法 定义 name 属性,并直接读取它:
🌐 Instead of this.props.name, define the name prop using the destructuring syntax and read it directly:
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}这是一个完整的示例:
🌐 Here is a complete example:
function Greeting({ name }) { return <h1>Hello, {name}!</h1>; } export default function App() { return ( <> <Greeting name="Sara" /> <Greeting name="Cahal" /> <Greeting name="Edite" /> </> ); }
将带有状态的组件从类迁移到函数
🌐 Migrating a component with state from a class to a function
假设你正在将这个 Counter 类组件转换为函数:
🌐 Suppose you’re converting this Counter class component to a function:
import { Component } from 'react'; export default class Counter extends Component { state = { name: 'Taylor', age: 42, }; handleNameChange = (e) => { this.setState({ name: e.target.value }); } handleAgeChange = (e) => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <> <input value={this.state.name} onChange={this.handleNameChange} /> <button onClick={this.handleAgeChange}> Increment age </button> <p>Hello, {this.state.name}. You are {this.state.age}.</p> </> ); } }
首先声明一个具有必要state variables:的函数
🌐 Start by declaring a function with the necessary state variables:
import { useState } from 'react';
function Counter() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
// ...接下来,转换事件处理程序:
🌐 Next, convert the event handlers:
function Counter() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
function handleNameChange(e) {
setName(e.target.value);
}
function handleAgeChange() {
setAge(age + 1);
}
// ...最后,将所有以 this 开头的引用替换为你在组件中定义的变量和函数。例如,将 this.state.age 替换为 age,将 this.handleNameChange 替换为 handleNameChange。
🌐 Finally, replace all references starting with this with the variables and functions you defined in your component. For example, replace this.state.age with age, and replace this.handleNameChange with handleNameChange.
这是一个完全转换的组件:
🌐 Here is a fully converted component:
import { useState } from 'react'; export default function Counter() { const [name, setName] = useState('Taylor'); const [age, setAge] = useState(42); function handleNameChange(e) { setName(e.target.value); } function handleAgeChange() { setAge(age + 1); } return ( <> <input value={name} onChange={handleNameChange} /> <button onClick={handleAgeChange}> Increment age </button> <p>Hello, {name}. You are {age}.</p> </> ) }
将带有生命周期方法的组件从类迁移到函数
🌐 Migrating a component with lifecycle methods from a class to a function
假设你正在将这个带有生命周期方法的 ChatRoom 类组件转换为函数组件:
🌐 Suppose you’re converting this ChatRoom class component with lifecycle methods to a function:
import { Component } from 'react'; import { createConnection } from './chat.js'; export default class ChatRoom extends Component { state = { serverUrl: 'https://localhost:1234' }; componentDidMount() { this.setupConnection(); } componentDidUpdate(prevProps, prevState) { if ( this.props.roomId !== prevProps.roomId || this.state.serverUrl !== prevState.serverUrl ) { this.destroyConnection(); this.setupConnection(); } } componentWillUnmount() { this.destroyConnection(); } setupConnection() { this.connection = createConnection( this.state.serverUrl, this.props.roomId ); this.connection.connect(); } destroyConnection() { this.connection.disconnect(); this.connection = null; } render() { return ( <> <label> Server URL:{' '} <input value={this.state.serverUrl} onChange={e => { this.setState({ serverUrl: e.target.value }); }} /> </label> <h1>Welcome to the {this.props.roomId} room!</h1> </> ); } }
首先,验证你的 componentWillUnmount 是否执行与 componentDidMount 相反的操作。在上面的例子中,这是正确的:它断开了 componentDidMount 建立的连接。如果缺少这样的逻辑,先添加它。
🌐 First, verify that your componentWillUnmount does the opposite of componentDidMount. In the above example, that’s true: it disconnects the connection that componentDidMount sets up. If such logic is missing, add it first.
接下来,验证你的 componentDidUpdate 方法是否处理了你在 componentDidMount 中使用的任何 props 和 state 的变化。在上面的示例中,componentDidMount 调用 setupConnection,它会读取 this.state.serverUrl 和 this.props.roomId。这就是为什么 componentDidUpdate 会检查 this.state.serverUrl 和 this.props.roomId 是否发生了变化,如果发生了变化就会重置连接。如果你的 componentDidUpdate 逻辑缺失或者没有处理所有相关的 props 和 state 的变化,首先修复它。
🌐 Next, verify that your componentDidUpdate method handles changes to any props and state you’re using in componentDidMount. In the above example, componentDidMount calls setupConnection which reads this.state.serverUrl and this.props.roomId. This is why componentDidUpdate checks whether this.state.serverUrl and this.props.roomId have changed, and resets the connection if they did. If your componentDidUpdate logic is missing or doesn’t handle changes to all relevant props and state, fix that first.
在上述例子中,生命周期方法内部的逻辑将组件连接到 React 之外的系统(聊天服务器)。要将组件连接到外部系统,将此逻辑描述为单个 Effect:
🌐 In the above example, the logic inside the lifecycle methods connects the component to a system outside of React (a chat server). To connect a component to an external system, describe this logic as a single Effect:
import { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}这个useEffect 调用等同于上面生命周期方法中的逻辑。如果你的生命周期方法做了多个不相关的操作,将它们拆分为多个独立的 Effects。这里有一个完整的示例,你可以尝试一下:
🌐 This useEffect call is equivalent to the logic in the lifecycle methods above. If your lifecycle methods do multiple unrelated things, split them into multiple independent Effects. Here is a complete example you can play with:
import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; export default function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; }, [roomId, serverUrl]); return ( <> <label> Server URL:{' '} <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} /> </label> <h1>Welcome to the {roomId} room!</h1> </> ); }
将带有上下文的组件从类迁移到函数
🌐 Migrating a component with context from a class to a function
在这个例子中,Panel 和 Button 类组件从 this.context: 读取 context
🌐 In this example, the Panel and Button class components read context from this.context:
import { createContext, Component } from 'react'; const ThemeContext = createContext(null); class Panel extends Component { static contextType = ThemeContext; render() { const theme = this.context; const className = 'panel-' + theme; return ( <section className={className}> <h1>{this.props.title}</h1> {this.props.children} </section> ); } } class Button extends Component { static contextType = ThemeContext; render() { const theme = this.context; const className = 'button-' + theme; return ( <button className={className}> {this.props.children} </button> ); } } function Form() { return ( <Panel title="欢迎"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } export default function MyApp() { return ( <ThemeContext value="dark"> <Form /> </ThemeContext> ) }
当你将它们转换为函数组件时,用 useContext 调用替换 this.context:
🌐 When you convert them to function components, replace this.context with useContext calls:
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); } function Form() { return ( <Panel title="欢迎"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } export default function MyApp() { return ( <ThemeContext value="dark"> <Form /> </ThemeContext> ) }