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 的更改引起。当重新渲染组件时,按以下顺序调用这些方法:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
这些方法被认为是遗留的,你应该在新代码中避免:
Unmounting(卸载)
当一个组件从 DOM 中删除时,将调用此方法:
错误处理
在生命周期方法中,或在任何子组件的构造函数中,渲染过程中出现错误时调用这些方法。
其他 APIs
每个组件还提供了一些其他 API:
类属性
实例属性
参考
常用的生命周期方法
本节中的方法涵盖了创建 React 组件时遇到的绝大多数用例。 对于可视化参考,请查看此生命周期图。
render()
render()
render()
方法是类组件中唯一必须的方法。
当被调用时,它会检查 this.props
和 this.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()
方法返回false
,render()
不会被调用。
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) 重命名为
initialColor
或defaultColor
是有意义的。 然后,你可以强制组件通过 改变它的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.props
与 nextProps
以及 this.state
与 nextState
,并返回 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(提交)” 阶段生命周期之间可能存在延迟(比如getSnapshotBeforeUpdate
和componentDidUpdate
)。
错误边界
错误边界 是React组件,它们可以在其子组件树的任何位置捕获 JavaScript 错误,记录这些错误,并显示回退 UI 而会让组件树崩溃。 错误边界在渲染期间,生命周期方法以及它们下面的整个树的构造函数中捕获错误。
如果类组件定义生命周期方法 static getDerivedStateFromError()
或 componentDidCatch()
中的任何一个(或两者),则它将成为错误边界。 从这些生命周期更新 state(状态),可让您在下面的树中捕获未处理的 JavaScript 错误并显示回退 UI 。
仅使用错误边界来从意外异常中恢复; 不要试图将它们用于控制流程。
有关更多详细信息,请参阅 React 16 中的错误处理。
注意
错误边界只能捕获树中它们 下面 组件中的错误。错误边界本身无法捕获错误。
static getDerivedStateFromError()
static getDerivedStateFromError(error)
在后代组件抛出错误后调用此生命周期。 它接收作为参数抛出的错误,并应返回值以更新 state(状态)。
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 };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
注意
在 “render” 阶段调用
getDerivedStateFromError()
,所以不允许有副作用(side-effects)。对于这些用例,应该使用componentDidCatch()
。
componentDidCatch()
componentDidCatch(error, info)
在后代组件抛出错误后调用此生命周期方法。 它接收两个参数:
error
- 抛出的错误。info
- 包含componentStack
键的对象,其中包含 有关哪个组件引发错误的信息 。
componentDidCatch()
在 “commit” 阶段被调用,因此允许副作用(side-effects)。 它应该用于记录错误之类的事情:
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) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logComponentStackToMyService(info.componentStack);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
注意
如果发生错误,您可以通过调用
setState
来渲染带有componentDidCatch()
的回退UI,但在将来的版本中将不推荐使用它。 使用static getDerivedStateFromError()
代替处理回退渲染。
遗留的生命周期方法
下面的生命周期方法被标记为 “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.props
和 nextProps
并使用 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
存在一个潜在的陷阱。
而使用 componentDidUpdate
或 setState
回调(setState(updater, callback)
),在应用更新后,都将被保证触发。如果你需要根据先前的 state 设置 state,阅读下面的 updater
参数。
setState()
总是会导致重新渲染,除非 shouldComponentUpdate()
返回 false
。如果可变对象被使用,并且条件渲染逻辑不能在 shouldComponentUpdate()
中实现,只有当新 state 与先前 state 不同时调用 setState()
才能避免不必要的重新渲染。
在这个签名中,第一个参数是的一个 updater
函数:
(state, props) => stateChange
state
是对先前 state 的引用。 它不会直接突变。 相反,应该根据输入的 state
和 props
构建一个新的对象来表示更改。 例如,假设我们想通过 props.step
在 state 中增加一个值:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
updater 函数接收到的 state
和 props
保证都是最新的。updater 输出的与 state
的浅层合并。
传递给 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((state) => {
return {counter: state.quantity + 1};
});
更多详情请参阅 :
- 状态(State) 和生命周期指南
- In depth: When and why are
setState()
calls batched? - In depth: Why isn’t
this.state
updated immediately?
forceUpdate()
component.forceUpdate(callback)
默认情况下,当组件的 state 或 props 改变时,组件将重新渲染。 如果你的 render()
方法依赖于一些其他数据,你可以告诉 React 组件需要通过调用 forceUpdate()
重新渲染。
调用 forceUpdate()
会导致组件跳过 shouldComponentUpdate()
,直接调用 render()
。 这将触发子组件的正常生命周期方法,包括每个子组件的 shouldComponentUpdate()
方法。 如果标记(markup)更改,React 仍将更新 DOM 。
通常你应该尽量避免使用 forceUpdate()
,并且 render()
中的 this.props
和 this.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
看作是不可变的。