React 的生命周期函数在组件的不同阶段发挥着至关重要的作用,深入理解这些函数能够帮助开发者更好地开发和优化 React 应用。下面我们来详细介绍几个重要的生命周期函数。
constructor
在 React 中,constructor 主要有以下几个作用:
- 初始化
state
。例如在代码中可以这样写,这里初始化了一个名为counter
的state
,初始值为0
。
constructor(props){
super(props);
this.state = { counter:0 };
}
- 纠正方法的 this 指向。可以通过
this.handleClick = this.handleClick.bind(this);
这样的方式将事件绑定在当前组件上,确保在事件处理函数中能够正确访问组件的属性和方法。如果使用 ES6 箭头函数,将不需要将事件在 constructor 中改变 this 指向,如<button onClick={() => this.click()}>纠正 this 指向</button>
。 - 如果有防抖、截流,也可以在这里做
getDerivedStateFromProps(静态方法)
getDerivedStateFromProps
是一个静态方法,在 React 组件的生命周期里承担着从props
派生state
的职责。React 将其设计为静态方法,旨在使其成为一个纯函数。这就意味着它不能有副作用,例如修改外部变量、发起网络请求等。每次调用这个函数时,都应仅依据传入的props
和当前的state
返回一个新的state
对象,并且相同的输入必须始终产生相同的输出。例如:
statice getDerivedStateFromProps(newProps) {
const { type } = newProps;
switch(type) {
case 'age':
return { name: '年龄', defaultValue: 18 };
case 'sex':
return { name: '性别', defaultValue: '男' };
}}
这个方法会在组件挂载(mount)以及每次接收新的props
导致组件更新时被调用。它在render
方法之前执行,这使得组件可以根据新的props
来调整自己的state
,从而影响渲染结果。
与其他生命周期的关系
componentWillReceiveProps
在 React 16.3 版本之前,componentWillReceiveProps
常用于在组件接收新props
时更新state
。componentWillReceiveProps
可以访问this.props
和this.state
,并且可以执行副作用操作,所以这个方法容易被滥用,导致一些难以发现的错误和性能问题;而getDerivedStateFromProps
是一个静态方法,不能访问this
,并且必须是纯函数,这样就限制了它的行为,使得组件的更新逻辑更加可预测。
shouldComponentUpdate
在组件更新流程中,getDerivedStateFromProps
在shouldComponentUpdate
之前被调用。它的返回值会与当前state
合并后作为参数传递给shouldComponentUpdate
,这使得shouldComponentUpdate
可以基于更新后的state
来决定组件是否需要真正进行重新渲染,从而在一定程度上优化组件的性能。
componentWillReceiveProps
componentWillReceiveProps
是一个在 React 组件生命周期中比较重要的方法。它在组件接收到新的props
时被调用,这发生在组件已经挂载(mounted)之后。在 React 16.3 版本之前,它是更新props
时常用的生命周期钩子。
从功能上来说,它允许组件在接收到新的props
后,根据新props
和旧props
的对比情况,来决定是否需要更新组件的state
,从而触发重新渲染。
这个生命周期方法在组件已经挂载到 DOM 中,并且接收到新的props
时触发。需要注意的是,这个方法在组件首次挂载时不会被调用,只有在后续props
发生变化时才会执行。它接收一个参数,即新的props
对象。在方法内部,可以通过this.props
访问旧的props
,这样就可以比较新旧props
的差异。
举个例子:
constructor(props) {
super(props);
this.state = {
count: props.initialCount
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.initialCount!== this.props.initialCount) {
this.setState({
count: nextProps.initialCount
});
}
}
componentWillUpdate
这个生命周期在组件接收到新的props
或者state
变化后,重新渲染之前被调用。这个方法提供了一个在组件更新之前执行某些操作的机会。当组件因为props
或state
的改变而需要重新渲染时,componentWillUpdate
就会被触发。需要注意的是,它不会在组件首次挂载时调用,只在更新阶段起作用。
componentWillUpdate
接收两个参数,分别是nextProps
和nextState
。nextProps
代表即将更新的props
,nextState
代表即将更新的state
。通过这两个参数,可以获取更新后的组件属性和状态信息。
使用场景
- 记录组件更新前的状态:可以利用
componentWillUpdate
来记录组件更新前的props
和state
,用于后续的比较或者其他用途。例如,在调试复杂的组件更新逻辑或者实现撤销 / 重做功能时,这种记录非常有用。 - 准备更新相关的数据或操作:在组件更新之前,可以在这个方法中进行一些准备工作。比如,如果组件中有一些基于
props
或state
计算得到的数据,而这些数据的计算成本较高,可以在componentWillUpdate
中提前计算,以便在render
阶段直接使用,提高渲染效率。不过需要注意的是,在这个方法中不应该调用setState
,因为这可能会导致无限循环的更新。 - 与动画效果结合:在一些需要添加动画效果的场景中,
componentWillUpdate
可以用于触发动画的开始。例如,当组件的大小或者位置因为props
或state
的变化而改变时,可以在componentWillUpdate
中设置动画的初始状态,然后在componentDidUpdate
中根据更新后的状态完成动画的过渡。
与 shouldComponentUpdate 关联
shouldComponentUpdate
决定组件是否需要更新,只有当shouldComponentUpdate
返回true
时,componentWillUpdate
才会被触发。这两个方法共同作用来控制组件的更新过程,shouldComponentUpdate
侧重于性能优化,通过比较新旧props
和state
来避免不必要的更新,而componentWillUpdate
侧重于更新前的准备工作。
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate
是在组件更新前(更确切地说是在最近一次渲染输出(提交到 DOM 节点)之前)被调用,这个方法使得组件能够在更新前获取一些信息,例如 DOM 元素的状态(如滚动位置、元素大小等)。它的主要目的是与componentDidUpdate
配合使用,计算形成一个 snapshot
传给 componentDidUpdate
,用于在组件更新过程中捕获一些可能会丢失的信息,以实现更平滑的更新体验,比如在更新列表时保留滚动位置等复杂操作。
举个例子:
class ScrollableList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
this.state = {
listData: []
};
}
componentDidMount() {
// 模拟获取数据并更新列表
this.setState({ listData: [1, 2, 3, 4, 5] });
}
getSnapshotBeforeUpdate(prevProps, prevState) {
if (this.listRef.current) {
return this.listRef.current.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot!== null) {
this.listRef.current.scrollTop = snapshot;
}
}
render() {
return (
<div ref={this.listRef}>
<ul>
{this.state.listData.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
}
componentDidUpdate
componentDidUpdate
是在组件完成更新后被调用。这意味着当组件的props
或者state
发生变化,导致组件重新渲染之后,componentDidUpdate
就会执行。每次组件成功更新后都会触发。但需要注意的是,组件首次挂载时不会调用componentDidUpdate
,因为它是专门用于处理更新后的操作。
componentDidUpdate
接收三个参数,分别是prevProps
(更新前的props
)、prevState
(更新前的state
)和snapshot
(getSnapshotBeforeUpdate
方法返回的值)。这些参数提供了更新前后的对比信息,方便开发者根据变化进行相应的操作。
componentDidMount
componentDidMount
是 React 组件生命周期中的一个关键阶段。它在组件被挂载(mounted)到 DOM 后立即被调用,这意味着组件已经完成了首次渲染并且其对应的 DOM 节点已经被插入到文档中。并且只会在组件第一次被添加到 DOM 时触发一次。无论组件是通过直接渲染还是在条件判断、循环等复杂逻辑下被渲染,只要是首次出现在 DOM 中,componentDidMount
就会执行。
componentDidMount
非常常见的一个用途就是进行网络请求。通常用于获取组件初始化所需的数据,如从服务器获取数据来填充列表、表单等。
还有就是如果组件需要使用第三方插件(如地图插件、富文本编辑器等),componentDidMount
是初始化这些插件的理想时机。
与其他生命周期的关系
componentWillMount
在旧版本的 React 中,有componentWillMount
这个生命周期方法。它在组件挂载之前被调用,而componentDidMount
在挂载之后。componentWillMount
在一些场景下可能会导致一些难以调试的问题,并且其功能在很大程度上可以被constructor
和componentDidMount
替代,所以 React 逐渐废弃了这个方法。componentDidMount
的优势在于可以确保 DOM 元素已经存在,能够安全地进行 DOM 操作和其他依赖 DOM 的操作。
componentDidUpdate
componentDidMount
只在组件首次挂载时执行,而componentDidUpdate
在每次组件更新后执行。它们都可以用于操作 DOM,但componentDidUpdate
更多地用于处理更新后的情况,例如根据更新后的props
或state
来调整 DOM 操作。同时,componentDidUpdate
可以利用componentDidMount
中初始化的一些资源(如订阅的事件、初始化的插件等),在更新后继续使用或调整。
shouldComponentUpdate
shouldComponentUpdate
在 React 组件生命周期中主要用于性能优化。它决定了组件在接收到新的props
或者state
改变时,是否应该进行重新渲染。通过合理地控制组件的重新渲染,可以避免不必要的 DOM 操作和计算,从而提高应用程序的性能。特别是在处理复杂组件树或者频繁更新的组件时,有效地使用shouldComponentUpdate
可以显著减少渲染开销。
在组件接收到新的props
或者state
发生变化时,shouldComponentUpdate
会在渲染之前被触发。这个方法在组件的更新流程中起着一个 “关卡” 的作用,决定组件是否能够进入实际的重新渲染阶段。它接收两个参数,nextProps
和nextState
,分别代表即将更新的props
和state
。通过比较nextProps
与当前props
(通过this.props
访问),以及nextState
与当前state
(通过this.state
访问),可以判断组件是否需要更新。
举个例子:
shouldComponentUpdate(nextProps, nextState) {
// 只有当count的值发生变化时,组件才重新渲染
return nextState.count!== this.state.count;
}
最后的最后
虽然 React 的生命周期函数曾经为组件的开发和优化提供了强大的支持,我们也通过对各个生命周期函数的深入了解更好地掌握了 React 组件的运行机制,并且在实际开发中合理运用它们来提高应用的性能和用户体验。但是,随着技术的不断发展,React 引入了更加简洁高效的 Hook。Hook 的出现为 React 开发带来了新的变革,让我们能够以更现代化的方式构建应用程序,提高开发效率和代码的可维护性。下一次,让我们再一起深入了解 React Hook 的强大之处。