React中父子组件生命周期的执行顺序
在 React 中,父子组件的生命周期执行顺序遵循一个清晰的规律:父组件先准备,子组件再挂载;子组件先卸载,父组件再清理。
核心原则:渲染阶段由父到子(render 递归),提交阶段(DOM 更新)再由子到父(componentDidMount/Update 递归)。
一、初始挂载阶段(Mounting)
顺序:父组件 getDerivedStateFromProps / render → 子组件生命周期 → 父组件 componentDidMount
详细步骤(以类组件为例):
- 父组件:constructor
- 父组件:getDerivedStateFromProps
- 父组件:render
→ 此时解析出子组件实例,开始子组件的生命周期 - 子组件:constructor
- 子组件:getDerivedStateFromProps
- 子组件:render
- 子组件:componentDidMount
- 父组件:componentDidMount
注意:componentDidMount 是子组件先执行,父组件后执行。
二、更新阶段(Updating)
触发条件:props 或 state 改变。
2.1 由父组件 state 变化引起的更新
- 父组件:getDerivedStateFromProps
- 父组件:shouldComponentUpdate
- 父组件:render
→ 重新生成子组件,触发子组件更新 - 子组件:getDerivedStateFromProps
- 子组件:shouldComponentUpdate
- 子组件:render
- 子组件:getSnapshotBeforeUpdate
- 子组件:componentDidUpdate
- 父组件:getSnapshotBeforeUpdate
- 父组件:componentDidUpdate
2.2 由子组件自身 state 变化引起的更新
只会触发子组件自身的更新生命周期,父组件不受影响(除非子组件通过回调修改父组件 state)。
三、卸载阶段(Unmounting)
顺序:父组件先标记卸载 → 子组件 componentWillUnmount → 父组件 componentWillUnmount
- 父组件:决定卸载子组件(例如条件渲染移除子组件)
- 子组件:componentWillUnmount
- 父组件:componentWillUnmount
子组件先执行清理工作,父组件最后清理。
四、函数组件中的近似顺序(useEffect)
函数组件没有直接等价的生命周期,但 useEffect 的执行顺序模拟了部分行为:
| 阶段 | 类组件顺序 | 函数组件近似顺序 |
|---|---|---|
| 挂载 | 父 render → 子 render → 子 didMount → 父 didMount | 父 render → 子 render → 子 useLayoutEffect → 父 useLayoutEffect → 子 useEffect → 父 useEffect |
| 更新 | 父 render → 子 render → 子 didUpdate → 父 didUpdate | 类似,但 cleanup 在上一次 effect 之前执行 |
| 卸载 | 子 willUnmount → 父 willUnmount | 子 useEffect cleanup → 父 useEffect cleanup |
注意:useEffect 是在浏览器完成布局与绘制之后异步执行,所以 useEffect 中父子顺序仍然是子先于父(因为组件树是深度优先渲染的)。
五、关键总结
- 挂载:父 render 先完成,但父 componentDidMount 要等所有子组件挂载完才执行。
- 更新:父 render 先执行,然后子组件更新,最后父 componentDidUpdate。
- 卸载:子组件先执行 componentWillUnmount,父组件最后执行。
- 函数组件:useEffect 的顺序是子先于父,但执行时间点在布局/绘制之后。
这个顺序保证了:
- 子组件在父组件访问它们之前已经准备好(或已清理)
- DOM 测量/滚动位置等操作可以在合适的时机进行
实例
以下是一个当前时间的实例,每秒更新:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, Runoob!</h1>
<h2>现在时间是:{this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.body);
root.render(
<Clock />
);以下实例在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {opacity: 1.0};
}
componentDidMount() {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
}
render () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Hello name="world"/>
);以下实例初始化 state , setNewnumber 用于更新 state。所有生命周期在 Content 组件中。
class Button extends React.Component {
constructor(props) {
super(props);
this.state = { data: 0 };
this.setNewNumber = this.setNewNumber.bind(this);
}
setNewNumber() {
this.setState({ data: this.state.data + 1 });
}
render() {
return (
<div>
<button onClick={this.setNewNumber}>INCREMENT</button>
<Content myNumber={this.state.data} />
</div>
);
}
}
class Content extends React.Component {
componentDidMount() {
console.log("Component DID MOUNT!");
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentDidUpdate(prevProps, prevState) {
console.log("Component DID UPDATE!");
}
componentWillUnmount() {
console.log("Component WILL UNMOUNT!");
}
render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<div>
<Button />
</div>
);到此这篇关于React中父子组件生命周期的执行顺序的文章就介绍到这了,更多相关React 父子组件执行顺序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论