组件之间的 React 生命周期事件顺序有哪些保证?
What are the guarantees on React lifecycle event order between components?
跨不同组件的 React 生命周期顺序保证是什么?它们是否在任何地方都有明确的记录?
例如,假设我有:
<div>{ x ? <A /> : <B /> }</div>
此处,x
在 true 和 false 之间切换将卸载一个组件并安装另一个组件。
是否保证关于安装中涉及的生命周期事件在这些元素中触发的顺序?
例如,well documented 当 x
变为真时,A 的 render
将在 A 的 componentDidMount
之前触发。但是,是否保证 A 的 render
和 componentDidMount
总是在 B 的 componentWillUnmount
之后触发?
更进一步:如果 A 和 B 在这棵树中 children 更靠下,开关在顶部,它会改变吗?
欢迎任何答案,但非常感谢关于此的公司文档。
有时我很难理解 componentWillMount() 和 componentDidMount() 方法之间的根本区别。当一个人工作时,我一直感到沮丧,但我不明白如何或为什么。
componentWillMount()
componentWillMount() 在 2018 年被弃用,直到一个在线学习小组 session 我才意识到的一个大障碍。虽然你可以继续使用它(直到 React 17.0),但它不是最佳实践,因为它对于异步渲染是不安全的。如果您选择继续使用它,您应该使用 UNSAFE_componentWillMount().
原因
在 componentWillMount() 中使用 fetch 调用会导致组件首先呈现空数据,因为 componentWillMount() 不会 return 在组件的第一次呈现之前。
由于 JavaScript 事件是异步的,当您进行 API 调用时,浏览器会在调用仍在进行时继续执行其他工作。使用 React,当组件正在渲染时,它不会等待 componentWillMount() 完成,所以组件会继续渲染。
综上所述,您需要创建一个组件,该组件在没有您希望显示的数据的情况下仍然看起来像样。在数据出现之前,没有办法(甚至没有计时器)停止组件渲染。
当我构建我的项目时,我不知道 componentWillMount() 的弃用,我不明白为什么我一直收到“无法读取未定义的 属性 'map'” 错误.我很困惑,因为我有一个数组,它应该被数据填充,但事实并非如此。
事实证明,这是完全合理的,因为组件最初是用空数据渲染的,所以数组是空的,无法迭代。只有在这一点上,我才知道使用 componentDidMount() 是最佳实践。
componentDidMount()
调用获取数据的最佳位置是在 componentDidMount() 中。 componentDidMount() 在客户端只被调用一次,而 componentWillMount() 被调用两次,一次在服务器端,一次在客户端。当客户端从服务器接收到数据并在浏览器中显示数据之前,在初始渲染之后调用它。由于在初始渲染后才会加载数据,因此开发人员需要正确设置组件的初始状态。
附带说明一下,借助 componentDidMount(),您可以使用计时器并每隔一段时间更新一次数据,而无需用户刷新页面。我想说这是一个非常巧妙的功能,可以在 projects/websites.
中使用。
componentDidUnmount
从未在条件渲染中调用。
我在 codepen 上测试并得到如下结果:
父组件
constructor(props) {
super(props);
this.state = {
flag: false,
}
}
render() {
const { flag } = this.state;
return (
<div>
<h2 onClick={() => this.setState({flag: !flag})}>CONVERT</h2>
<div>
{ flag ? <A /> : <B /> }
</div>
</div>
);
}
子组件 A
class A extends React.Component {
componentDidMount() {
console.log('A is mounted');
}
componentWillMount() {
console.log('A will be mounted');
}
componentWillUnmount() {
console.log('A will be unmounted');
}
componentDidUnmount() {
console.log('A is unmounted');
}
render() {
return (
<h1>A</h1>
);
}
}
子组件 B
Almost the same as A :D
结果
我点击了两次CONVERT
,结果是:
"B will be mounted" // first render
"B is mounted"
"B will be unmounted" // first click
"A will be mounted"
"A is mounted"
"A will be unmounted" // second click
"B will be mounted"
"B is mounted"
componentDidUnmount
was never called in conditional rendering.
如果你查看 React 官方仓库中的测试,你可以轻松找到与此订单相关的相关测试。
it('prepares new child before unmounting old', () => {
const log = [];
class Spy extends React.Component {
UNSAFE_componentWillMount() {
log.push(this.props.name + ' componentWillMount');
}
render() {
log.push(this.props.name + ' render');
return <div />;
}
componentDidMount() {
log.push(this.props.name + ' componentDidMount');
}
componentWillUnmount() {
log.push(this.props.name + ' componentWillUnmount');
}
}
class Wrapper extends React.Component {
render() {
return <Spy key={this.props.name} name={this.props.name} />;
}
}
const container = document.createElement('div');
ReactDOM.render(<Wrapper name="A" />, container);
ReactDOM.render(<Wrapper name="B" />, container);
expect(log).toEqual([
'A componentWillMount',
'A render',
'A componentDidMount',
'B componentWillMount',
'B render',
'A componentWillUnmount',
'B componentDidMount',
]);
});
The reasoning behind this implementation can be found @ commit
message for test
"This matches what we do in Fiber -- and doing it this way is the only
way we can prepare new views in the background before unmounting old
ones."
同时查看 React 16.0.0 的最新更新日志:React 16.0.0 changelog
When replacing <A /> with <B />, B.componentWillMount now always
happens before A.componentWillUnmount. Previously,
A.componentWillUnmount could fire first in some cases.
所以这个顺序是有保证的!
跨不同组件的 React 生命周期顺序保证是什么?它们是否在任何地方都有明确的记录?
例如,假设我有:
<div>{ x ? <A /> : <B /> }</div>
此处,x
在 true 和 false 之间切换将卸载一个组件并安装另一个组件。
是否保证关于安装中涉及的生命周期事件在这些元素中触发的顺序?
例如,well documented 当 x
变为真时,A 的 render
将在 A 的 componentDidMount
之前触发。但是,是否保证 A 的 render
和 componentDidMount
总是在 B 的 componentWillUnmount
之后触发?
更进一步:如果 A 和 B 在这棵树中 children 更靠下,开关在顶部,它会改变吗?
欢迎任何答案,但非常感谢关于此的公司文档。
有时我很难理解 componentWillMount() 和 componentDidMount() 方法之间的根本区别。当一个人工作时,我一直感到沮丧,但我不明白如何或为什么。
componentWillMount() componentWillMount() 在 2018 年被弃用,直到一个在线学习小组 session 我才意识到的一个大障碍。虽然你可以继续使用它(直到 React 17.0),但它不是最佳实践,因为它对于异步渲染是不安全的。如果您选择继续使用它,您应该使用 UNSAFE_componentWillMount().
原因 在 componentWillMount() 中使用 fetch 调用会导致组件首先呈现空数据,因为 componentWillMount() 不会 return 在组件的第一次呈现之前。
由于 JavaScript 事件是异步的,当您进行 API 调用时,浏览器会在调用仍在进行时继续执行其他工作。使用 React,当组件正在渲染时,它不会等待 componentWillMount() 完成,所以组件会继续渲染。
综上所述,您需要创建一个组件,该组件在没有您希望显示的数据的情况下仍然看起来像样。在数据出现之前,没有办法(甚至没有计时器)停止组件渲染。
当我构建我的项目时,我不知道 componentWillMount() 的弃用,我不明白为什么我一直收到“无法读取未定义的 属性 'map'” 错误.我很困惑,因为我有一个数组,它应该被数据填充,但事实并非如此。
事实证明,这是完全合理的,因为组件最初是用空数据渲染的,所以数组是空的,无法迭代。只有在这一点上,我才知道使用 componentDidMount() 是最佳实践。
componentDidMount() 调用获取数据的最佳位置是在 componentDidMount() 中。 componentDidMount() 在客户端只被调用一次,而 componentWillMount() 被调用两次,一次在服务器端,一次在客户端。当客户端从服务器接收到数据并在浏览器中显示数据之前,在初始渲染之后调用它。由于在初始渲染后才会加载数据,因此开发人员需要正确设置组件的初始状态。
附带说明一下,借助 componentDidMount(),您可以使用计时器并每隔一段时间更新一次数据,而无需用户刷新页面。我想说这是一个非常巧妙的功能,可以在 projects/websites.
中使用。componentDidUnmount
从未在条件渲染中调用。
我在 codepen 上测试并得到如下结果:
父组件
constructor(props) {
super(props);
this.state = {
flag: false,
}
}
render() {
const { flag } = this.state;
return (
<div>
<h2 onClick={() => this.setState({flag: !flag})}>CONVERT</h2>
<div>
{ flag ? <A /> : <B /> }
</div>
</div>
);
}
子组件 A
class A extends React.Component {
componentDidMount() {
console.log('A is mounted');
}
componentWillMount() {
console.log('A will be mounted');
}
componentWillUnmount() {
console.log('A will be unmounted');
}
componentDidUnmount() {
console.log('A is unmounted');
}
render() {
return (
<h1>A</h1>
);
}
}
子组件 B
Almost the same as A :D
结果
我点击了两次CONVERT
,结果是:
"B will be mounted" // first render
"B is mounted"
"B will be unmounted" // first click
"A will be mounted"
"A is mounted"
"A will be unmounted" // second click
"B will be mounted"
"B is mounted"
componentDidUnmount
was never called in conditional rendering.
如果你查看 React 官方仓库中的测试,你可以轻松找到与此订单相关的相关测试。
it('prepares new child before unmounting old', () => {
const log = [];
class Spy extends React.Component {
UNSAFE_componentWillMount() {
log.push(this.props.name + ' componentWillMount');
}
render() {
log.push(this.props.name + ' render');
return <div />;
}
componentDidMount() {
log.push(this.props.name + ' componentDidMount');
}
componentWillUnmount() {
log.push(this.props.name + ' componentWillUnmount');
}
}
class Wrapper extends React.Component {
render() {
return <Spy key={this.props.name} name={this.props.name} />;
}
}
const container = document.createElement('div');
ReactDOM.render(<Wrapper name="A" />, container);
ReactDOM.render(<Wrapper name="B" />, container);
expect(log).toEqual([
'A componentWillMount',
'A render',
'A componentDidMount',
'B componentWillMount',
'B render',
'A componentWillUnmount',
'B componentDidMount',
]);
});
The reasoning behind this implementation can be found @ commit message for test
"This matches what we do in Fiber -- and doing it this way is the only way we can prepare new views in the background before unmounting old ones."
同时查看 React 16.0.0 的最新更新日志:React 16.0.0 changelog
When replacing <A /> with <B />, B.componentWillMount now always
happens before A.componentWillUnmount. Previously,
A.componentWillUnmount could fire first in some cases.
所以这个顺序是有保证的!