React.Component

此页面包含了 React 组件类定义的详细 API 参考。 我们已经假设你熟悉了基本的 React 概念, 如组件(Components) 和 属性(Props) , 以及 状态(State) 和 生命周期 。 如果你不是很熟悉,请先阅读它们。

概述

React 可以将组件定义为 类 或 函数 。定义为类组件当前提供了更多的功能,这些功能将在本页面中详细介绍。 要定义React组件类,需要扩展 React.Component

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

您必须在 React.Component 子类中定义的唯一方法是render()。此页面上描述的所有其他方法都是可选的。

我们不建议创建你自己的基础组件类。 在 React 组件中,代码重用主要通过组合而不是继承来实现

注意:

React不会强制您使用 ES6 类(class) 语法。 如果你想避免它,你可以使用 create-react-class 模块或类似的自定义抽象。看看不使用 ES6 的React 了解更多信息。

组件的生命周期

每个组件都有几个“生命周期方法”,您可以重写这些方法,以在过程中的特定时间运行代码。 您可以使用 此生命周期图 作为备忘。 在下面的列表中,常用的生命周期方法标记为 bold。 其余的生命周期方法通常用于相对罕见的用例。

Mounting(装载)

当组件实例被创建并将其插入 DOM 时,将按以下顺序调用这些方法:

注意:

这些方法被认为是遗留的,你应该在新代码中避免

Updating(更新)

更新可以由对 props 或 state 的更改引起。当重新渲染组件时,按以下顺序调用这些方法:

注意:

这些方法被认为是遗留的,你应该在新代码中避免

Unmounting(卸载)

当一个组件从 DOM 中删除时,将调用此方法:

错误处理

在生命周期方法中,或在任何子组件的构造函数中,渲染过程中出现错误时调用此方法。

其他 APIs

每个组件还提供了一些其他 API:

类属性

实例属性


参考

常用的生命周期方法

本节中的方法涵盖了创建 React 组件时遇到的绝大多数用例。 对于可视化参考,请查看此生命周期图

render()

render()

render() 方法是类组件中唯一必须的方法。

当被调用时,它会检查 this.propsthis.state 并返回其中一个类型:

  • React元素 通常是由 JSX 创建。该元素可能是一个原生DOM组件的表示,例如,<div /><MyComponent /> 是React元素,它告诉 React 分别渲染 DOM 节点或其他用户定义的组件。
  • 数组和片段(fragments). 让您从渲染中返回多个元素。 有关更多详细信息,请参阅 片段(fragments) 上的文档。
  • Portals 让您将子元素渲染到不同的 DOM 子树中。 有关更多详细信息,请参见 插槽(portals) 上的文档。
  • 字符串和数字 这些将被渲染为 DOM 中的 text 节点。
  • 布尔值 或 null 不渲染任何东西。(通常存在于 return test && <Child /> 模式,其中 test 是布尔值。)

render() 函数应该是纯函数,这意味着它不会修改组件状态,每次调用它时返回相同的结果,它不会直接与浏览器交互。

如果您需要与浏览器交互,请改用 componentDidMount() 或其他生命周期方法执行你的工作。 保持render() 为纯函数使得组件更容易理解。

注意:

如果 shouldComponentUpdate() 方法返回 falserender() 不会被调用。


constructor()

constructor(props)

如果你没有初始化 状态(state) ,并且没有绑定方法,你不需要为你的 React 组件实现一个构造函数。

React 组件的构造函数在 挂载(mounted) 之前被调用。 在实现 React.Component 子类的构造函数时, 你应该在任何其他声明之前调用super(props)。 否则,this.props 将在 constructor(构造函数) 中是 undefined(未定义) ,这将导致 bug 。

通常,React 构造函数只用于两个目的:

不应该在 constructor() 中调用 setState()。相反,如果您的组件需要使用本地 state,直接在构造函数中 将初始状态赋给 this.state 即可

constructor(props) {
  super(props);
  // Don't call this.setState() here!
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

构造函数是你应该直接分配 this.state 的唯一地方。 在所有其他方法中,您需要使用 this.setState() 来代替。

避免在构造函数中引入任何 副作用(side-effects) 或 订阅(subscriptions) 。对于那些用例,使用 componentDidMount() 来代替。

构造函数是初始化 状态(state) 的正确位置。 为此,只需将一个对象分配给 this.state ; 不要尝试在构造函数上调用 setState() 。 构造函数也经常用于将事件处理程序绑定到类实例。

Note

避免复制 属性(props) 到 状态(state) ! 这是一个常见的错误:

constructor(props) {
 super(props);
 // Don't do this!
 this.state = { color: props.color };
}

问题是,这是不必要的(直接使用 this.props.color 代替),并会产生错误(对 color 属性(props)的更新不会反映在状态中)。

仅在你故意要忽略 属性(props)更新时,使用此模式。 在这种情况下,将 属性(props) 重命名为 initialColordefaultColor 是有意义的。 然后,你可以强制组件通过 改变它的key 来重置它的内部状态。

如果你认为你需要一些状态(state)来依赖于这个属性(props),阅读我们的 避免派生 状态(state) 的博客文章,以了解你需要做什么。


componentDidMount()

componentDidMount()

componentDidMount() 在组件装载(mounting)(插入树)后被立即调用。初始化所需要的 DOM 节点的应该放在这里。 如果你需要从远程加载数据,这是一个实例化网络请求的好地方。

这种方法是设置任何 订阅(subscriptions) 的好地方。如果你这样做,不要忘了在 componentWillUnmount() 中 取消订阅(unsubscribe) 。

你可以立刻在 componentDidMount() 中调用setState()。 它会触发额外的渲染, 但会在浏览器更新屏幕之前发生。在这种情况下,即使 render() 会被调用两次, 也可以保证用户不会看到中间状态。 请谨慎使用此模式,因为这通常会导致性能问题。 在大多数情况下,您应该能够在 constructor() 中分配初始 state(状态) 。但是,当你需要测量一个 DOM 节点,并在渲染一些依赖于它的大小或位置的东西之前,这种情况下,这种模式可能会非常有用。


componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 在更新发生后立即被调用。 这个方法在第一次渲染时不会被调用。

当组件已更新时,使用此方法作为操作 DOM 的一个机会。 这也是做网络请求的一个好地方,只需你比较当前 props 与以前的 props(例如,如果 props 没有改变,可能不需要网络请求)。

componentDidUpdate(prevProps) {
  // Typical usage (don't forget to compare props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

你可以在 componentDidUpdate()立即调用 setState() ,但请注意,必须包含在条件语句中 像上面的例子一样,否则你会导致无限循环。 这也会导致额外的重新渲染, 虽然对用户不可见,但是会影响组件的性能。 如果你试图将某些 state(状态) “镜像”到来自上面的 属性(props) 中, 请考虑直接使用 属性(props) 。 阅读更多关于 为什么将 属性(props) 复制到 state(状态) 会导致错误

如果你的组件实现了 getSnapshotBeforeUpdate() 生命周期(很少见), 它返回的值将作为第三个 snapshot 参数传递给 componentDidUpdate() 。 否则这个参数将是 undefined 。

注意:

如果 shouldComponentUpdate() 返回 false ,那么 componentDidUpdate() 不会被调用。


componentWillUnmount()

componentWillUnmount()

componentWillUnmount() 在一个组件被 卸载(unmounted) 和 销毁(destroyed) 之前立即被调用。 在此方法中执行任何必要的清理,例如使计时器无效,取消网络请求,或清理在 componentDidMount 中创建的任何 DOM 元素。

不应该在 componentWillUnmount() 中调用 setStat() ,因为组件不会被重新渲染。 一旦组件实例卸载(unmounted)后,将永远不会再次装载(mounted)它。


很少使用的生命周期方法

本节中的方法对应于不常见的用例。 他们肯能会偶尔很方便, 但大多数组件可能不需要它们。 此生命周期图 中,如果您单击顶部的“Show less common lifecycles(显示不太常用的生命周期)”复选框,可以看到以下大多数方法。

shouldComponentUpdate()

shouldComponentUpdate(nextProps, nextState)

使用 shouldComponentUpdate() 让 React 知道组件的输出是否不受 state(状态) 或 属性(props) 当前变化的影响。 默认行为是在每次 state(状态) 更改时重新渲染,并且在绝大多数情况下,你应该依赖于默认行为。

当接收到新的 属性(props) 或 state(状态) 时,shouldComponentUpdate() 在渲染之前被调用。 默认返回 true ,对于初始(第一次)渲染 或 使用 forceUpdate() 时,不调用此方法。

此方法仅作为** 性能优化存在。** 不要依靠它来“防止”渲染,因为这可能会导致错误。 考虑使用内置的 PureComponent 而不是手动编写shouldComponentUpdate()PureComponent 对 属性(props) 和 state(状态) 进行浅层比较, 并减少您跳过必要更新的机会。

如果你确信你想手工编写它,你可以比较 this.propsnextProps 以及 this.statenextState,并返回 false 告诉 React 可以跳过这次更新。

我们不推荐进行深度相等检查或者在 shouldComponentUpdate() 中使用JSON.stringify() 。这是非常低效的,会降低性能。

目前,如果 shouldComponentUpdate() 返回 false ,那么 UNSAFE_componentWillUpdate()render()componentDidUpdate() 将不会被调用。 注意,在将来 React 可能将 shouldComponentUpdate() 作为暗示而不是严格的指令,也就是说,返回 false 可能仍然导致组件的重新渲染。


static getDerivedStateFromProps()

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 在调用 render 方法之前被调用,包括初始装载(mount)和后续更新时。 它应该返回一个更新 state(状态) 的对象,或者返回 null 以不更新任何 state(状态)。

这种方法适用于罕见用例 ,其 state(状态) 取决于 属性(props) 随着时间的推移而改变。 例如,实现一个 <Transition> 组件可能会比较方便,该组件比较其前一个和下一个子 state (状态),以决定哪些子元素可以进入和退出。

导出状态会导致冗长的代码并使您的组件难以理解。 确保你熟悉更简单的选择方法:

  • 如果您需要 执行副作用(side effect)(例如,数据获取或动画)以响应 属性(props) 的更改,使用 componentDidUpdate 生命周期方法。

  • 如果你只想 在属性(props)改变 时重新计算一些数据,请使用 memoization 辅助工具

  • 如果你只想 在属性(props)改变时 “重置” 一些 state(状态), 考虑使用一个 完全控制组件  或 一个key的完全不受控组件。

此方法无权访问组件实例。如果你愿意,你可以在getDerivedStateFromProps()和其他类方法之间重用一些代码,方法是在类定义之外提取组件 属性(props) 和 state(状态) 的纯函数。

请注意,无论原因如何,此方法都会在 每个 render 上触发。这与 UNSAFE_componentWillReceiveProps 形成对比,UNSAFE_componentWillReceiveProps 仅在父组件进行重新渲染时触发,而不是作为本地 setState 的结果。

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染输出被提交之前调用。它使您的组件能够在DOM发生潜在变化之前捕获一些信息(例如滚动位置)。此生命周期返回的任何值将作为参数传递给componentDidUpdate()

这个用例并不常见, 但它可能会出现在需要以特殊方式处理滚动位置的聊天线程之类的UI中。

一个 snapshot(快照)值(或null)应该被返回。

例如:

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}>{/* ...contents... */}</div>
    );
  }
}

在上面的例子中,读取 getSnapshotBeforeUpdate 中的scrollHeight属性是很重要的,因为在 “render” 阶段生命周期(比如render)和 “commit(提交)” 阶段生命周期之间可能存在延迟(比如getSnapshotBeforeUpdatecomponentDidUpdate)。


componentDidCatch()

componentDidCatch(error, info)

错误边界(Error boundaries) 是一个 React 组件,可以在其子组件树中的任何位置捕获JavaScript 错误,记录这些错误,并显示备用 UI 而不是崩溃的组件树。 错误边界在渲染过程中,在生命周期方法中,以及整个树下的构造函数中捕获错误。

如果一个类组件定义了这个生命周期方法,那么它将成为一个错误边界组件。调用它的 setState() 可以让您捕获树下面未处理的 JavaScript 错误,并显示一个备用 UI 。只能使用 错误边界组件 从意外的异常中恢复;不要试图将它们用于控制流程。

更多信息请查看错误边界(Error Boundaries)React 16 中的错误处理.

注意:

错误边界(Error Boundaries) 只能捕获组件树中 子组件 中的错误。不能捕获 错误边界(Error Boundaries)组件 本身错误。


遗留的生命周期方法

下面的生命周期方法被标记为 “legacy(遗留)”。 他们仍然有效,但我们不建议在新代码中使用它们。 您可以在 这篇博客文章 中了解有关从旧版生命周期方法迁移的更多信息。

UNSAFE_componentWillMount()

UNSAFE_componentWillMount()

UNSAFE_componentWillMount() 在组件 装载(mounting) 发生之前立即被调用。它在 render() 之前调用, 因此在此方法中同步调用 setState() 不会触发额外的渲染。 通常,我们建议使用 constructor() 来代替初始化状态。

避免在此方法中引入任何 side-effects 或 subscriptions(订阅)。 对于这些用例,请改用componentDidMount()

这是在服务器渲染上调用的唯一生命周期钩子。

注意:

此生命周期以前被命名为 componentWillMount 。该名称将继续工作,直到 17 版本。使用 rename-unsafe-lifecycles codemod 自动更新组件。


UNSAFE_componentWillReceiveProps()

UNSAFE_componentWillReceiveProps(nextProps)

注意:

使用此生命周期方法通常会导致错误和不一致,因此将来会被弃用。

如果您需要 执行一个 side effect(例如,数据获取或动画)以响应 属性(props) 的更改,请改用componentDidUpdate 生命周期方法代替。

对于其他用例,请按照这篇博客文章中有关派生 状态(state) 的建议

如果你使用componentWillReceiveProps在属性(props)改变 时重新计算一些数据,请使用 memoization 辅助工具

如果你使用 componentWillReceiveProps在属性(props)改变时 “重置” 一些 state(状态),考虑使用一个 完全控制组件 或 一个 key的完全不受控 组件。

在极少数情况下,您可能希望将getDerivedStateFromProps生命周期作为最后的手段。

UNSAFE_componentWillReceiveProps() 在已装载组件接收新 props 之前被调用。 如果您需要更新 state 以响应 props 的更改(例如,重置它),则可以在此方法中比较this.propsnextProps 并使用 this.setState() 执行状态转换。

注意,即使 prop 没有改变,React 也可能调用这个方法,因此如果你只想处理变化,请确保比较当前值和下一个值。 当父组件导致你的组件重新渲染时,可能会发生这种情况。

装载(mounting) 期间,React 不会用初始的 props 调用 UNSAFE_componentWillReceiveProps() 。如果某些组件的 props 可能更新,它只会调用此方法。调用 this.setState() 一般不会触发 UNSAFE_componentWillReceiveProps()

注意:

此生命周期以前被命名为 componentWillReceiveProps 。该名称将继续工作,直到 17 版本。使用 rename-unsafe-lifecycles codemod 自动更新组件。


UNSAFE_componentWillUpdate()

UNSAFE_componentWillUpdate(nextProps, nextState)

当接收到新的 props 或 state 时,UNSAFE_componentWillUpdate() 在渲染之前立即被调用。在更新发生之前,使用这个方法可以作为执行准备更新的一个好机会。这个方法在第一次渲染时不会被调用。

注意,这里不能调用 this.setState() 。你也不应该做任何其他的事情(例如,委派一个 Redux 动作),它会在UNSAFE_componentWillUpdate() 返回之前触发一个 React 组件的更新。

通常,此方法可以使用 componentDidUpdate() 代替。 如果您正在使用此方法读取 DOM(例如,保存滚动位置),则可以将该逻辑移至 getSnapshotBeforeUpdate()

注意:

此生命周期以前被命名为 componentWillUpdate 。该名称将继续工作,直到 17 版本。使用 rename-unsafe-lifecycles codemod 自动更新组件。

注意:

如果 shouldComponentUpdate() 返回 false ,那么 UNSAFE_componentWillUpdate() 不会被调用。


其他API

与上面的生命周期方法(React为你调用)不同,下面的方法是 可以在组件中调用的方法。

他们只有两个:setState()forceUpdate()

setState()

setState(updater, [callback])

setState() 排队更改组件的 state ,并通过更新 state 来告诉 React ,该组件及其子组件需要重新渲染。这是用于 响应事件处理程序 和 服务器响应 更新用户界面的主要方法。

记住 setState() 作为一个请求,而不是立即命令来更新组件。为了更好的感知性能,React 可能会延迟它,然后合并多个setState()更新多个组件。 React不保证 state 更新就立即应用(重新渲染)。

setState() 并不总是立即更新组件。它可能会 批量 或 延迟到后面更新。这使得在调用 setState() 之后立即读取 this.state 存在一个潜在的陷阱。 而使用 componentDidUpdatesetState 回调(setState(updater, callback)),在应用更新后,都将被保证触发。如果你需要根据先前的 state 设置 state,阅读下面的 updater 参数。

setState() 总是会导致重新渲染,除非 shouldComponentUpdate() 返回 false 。如果可变对象被使用,并且条件渲染逻辑不能在 shouldComponentUpdate() 中实现,只有当新 state 与先前 state 不同时调用 setState() 才能避免不必要的重新渲染。

在这个签名中,第一个参数是的一个 updater 函数:

(prevState, props) => stateChange

prevState 是对先前 state 的引用。 它不会直接突变。 相反,应该根据输入的 prevStateprops 构建一个新的对象来表示更改。 例如,假设我们想通过 props.step 在 state 中增加一个值:

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

updater 函数接收到的 prevStateprops 保证都是最新的。updater 输出的与 prevState 的浅层合并。

传递给 setState() 的第二个参数是一个可选的回调函数,这个回调函数将在 setState 完成后执行,并且重新渲染组件。通常,这样的逻辑我们建议使用 componentDidUpdate()

您可以随意的传递 一个对象 作为 setState() 的第一个参数,而不是一个函数:

setState(stateChange, [callback])

这将执行 stateChange 的浅合并到新的 state ,例如,调整购物车物品数量:

this.setState({quantity: 2})

这种形式的 setState() 也是异步的,并且在同一周期内的多个调用可以被合并在一起执行批处理。例如,如果您尝试在同一周期内多次增加项目数量,这将导致的结果相当于:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

同一周期中,后续调用将覆盖先前调用的值,所以数量只会增加一次。如果下一个 state 取决于以前的 state ,我们推荐使用 updater 函数形式:

this.setState((prevState) => {
  return {counter: prevState.quantity + 1};
});

更多详情请参阅 :


forceUpdate()

component.forceUpdate(callback)

默认情况下,当组件的 state 或 props 改变时,组件将重新渲染。 如果你的 render() 方法依赖于一些其他数据,你可以告诉 React 组件需要通过调用 forceUpdate() 重新渲染。

调用 forceUpdate() 会导致组件跳过 shouldComponentUpdate() ,直接调用 render()。 这将触发子组件的正常生命周期方法,包括每个子组件的 shouldComponentUpdate() 方法。 如果标记(markup)更改,React 仍将更新 DOM 。

通常你应该尽量避免使用 forceUpdate() ,并且 render() 中的 this.propsthis.state 应该是只读的。


类属性(Class Properties)

defaultProps

defaultProps 可以定义为组件类自身的属性,用来设置类的默认 props 。 这用于未定义的(undefined) props,但不用于 null props 。 例如:

class CustomButton extends React.Component {
  // ...
}

CustomButton.defaultProps = {
  color: 'blue'
};

如果没有提供 props.color ,它将默认设置为'blue'

  render() {
    return <CustomButton /> ; // props.color 将被设置为 blue
  }

如果 props.color 设置为 null,它将保持为 null

  render() {
    return <CustomButton color={null} /> ; // props.color 将保持 null
  }

displayName

displayName字符串用于调试消息。 通常,您不需要明确设置它,因为它是根据定义组件的函数或类的名称推断出来的。 如果您想为调试目的显示不同的名称或者创建高阶组件时,可能需要显式设置它, 有关详细信息,请参阅 包装显示名称以便轻松调试


实例属性(Instance Properties)

props

this.props 包含此组件的调用者定义的 props 。 有关 props 的介绍,请参阅组件(Components) 和 属性(Props)

特别要说明的是,this.props.children 是一个特殊的 props ,通常由JSX表达式中的子标签定义,而不是标签本身。

state

state(状态) 包含该组件的的特定数据,该数据可能随时间而变化。 状态是用户定义的,它应该是一个纯粹的 JavaScript 对象。

如果你不在 render() 中使用它,它就不应该是 state 。 例如,您可以直接在实例上放置定时器ID。

有关 state(状态) 的详细信息,请参阅 状态(State) 和生命周期

永远不要直接改变 this.state ,因为调用 setState() 之后可能会覆盖你所做的这个改变。 把 this.state 看作是不可变的。