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 的强大之处。