> 文章列表 > 0207生命周期v17.x-组件-React

0207生命周期v17.x-组件-React

0207生命周期v17.x-组件-React

1 新版生命周期

React17.x生命周期如下图1-1所示:

在这里插入图片描述

React组件的生命周期可以分为三个主要阶段:挂载阶段,更新阶段和卸载阶段。每个阶段都有不同的生命周期方法可供使用。

  1. 挂载阶段(Mounting)

在挂载阶段,组件将被插入到DOM中。以下是挂载阶段的生命周期方法:

  • constructor():组件被创建时调用,初始化组件的状态和绑定方法。
  • static getDerivedStateFromProps():在组件挂载之前和更新时调用,可以根据新的props计算state。
  • render():在组件挂载之前和更新时调用,返回虚拟DOM,用于渲染到页面。
  • componentDidMount():在组件挂载后调用,可以执行DOM操作或发起网络请求等操作。
  1. 更新阶段(Updating)

在更新阶段,组件的props或state被修改,导致重新渲染。以下是更新阶段的生命周期方法:

  • static getDerivedStateFromProps():在组件更新时调用,可以根据新的props计算state。
  • shouldComponentUpdate():在组件更新时调用,决定是否需要重新渲染组件,可以优化性能。
  • render():在组件更新时调用,返回虚拟DOM,用于渲染到页面。
  • getSnapshotBeforeUpdate():在组件更新之前调用,可以获取更新前的DOM状态。
  • componentDidUpdate():在组件更新后调用,可以执行DOM操作或发起网络请求等操作。
  1. 卸载阶段(Unmounting)

在卸载阶段,组件将从DOM中移除。以下是卸载阶段的生命周期方法:

  • componentWillUnmount():在组件卸载前调用,可以执行清理操作,如取消网络请求或定时器等。

还有一些其他的生命周期方法,如错误处理的 componentDidCatch() 和 getDerivedStateFromError() 方法。这些方法可以帮助开发者更好地管理组件的生命周期和错误处理。

2 与旧版对比

React 17.x版本没有引入新的生命周期方法,但是对生命周期方法的行为进行了一些改变。

  1. 生命周期方法的弃用

React 17.x废弃了三个生命周期方法:componentWillMount()、componentWillReceiveProps()、componentWillUpdate()。这些方法在React 16.x版本中仍然可用,但在React 17.x版本中不推荐使用。取而代之的是,React 17.x推荐使用新的静态方法getDerivedStateFromProps()和getDerivedStateFromError()来代替。

  1. 组件更新

在React 16.x版本中,React会在shouldComponentUpdate()方法返回true后,调用render()方法和componentDidUpdate()方法。但是在React 17.x版本中,如果shouldComponentUpdate()方法返回false,React仍然会调用render()方法,但是不会调用componentDidUpdate()方法。这个改变使得React在一些情况下更加灵活,同时也提高了性能。

  1. 事件处理器的绑定

在React 17.x版本中,React要求所有事件处理器的绑定必须在构造函数中完成。这个改变可以使得React在更新时更加高效,并且减少了一些错误的发生。

综上所述,React 17.x版本在生命周期方法的行为上与React 16.x版本相比并没有太大的变化,但是在一些细节上进行了改进,以提高性能和可靠性。同时,React 17.x版本也为未来的版本打下了基础,使得React可以更好地适应新的技术和应用场景。

测试代码同之前一致,限制只是把js换成React17.0.1,旧版生命周期函数在17.x中仍然可以使用,但是由警告,如下所示:

react-dom.development.js:61 Warning: componentWillMount has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.* Move code with side effects to componentDidMount, and set initial state in the constructor.
* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

点击链接,跳转到官网。大意是在之后发布的版本中,这3个函数会加上UNSAFE_前缀。不是代表它们不安全,而是在未来的版本中使用可能由bug,特别是在异步渲染中。

3 getDerivedStateFromProps

getDerivedStateFromProps是React生命周期方法之一,用于根据props计算和更新state。该方法在组件挂载和更新时都会被调用。

getDerivedStateFromProps(props, state)方法接收两个参数,props表示组件的props,state表示组件的state。该方法必须返回一个对象,用于更新组件的state。

在React 16.x版本中,getDerivedStateFromProps通常被用于解决props和state不同步的问题,特别是当组件的props和state都受到外部因素的影响时。例如,当组件从父组件中接收props并更新自己的state时,可以使用getDerivedStateFromProps方法来保持props和state的同步。

在React 17.x版本中,getDerivedStateFromProps方法不再推荐用于处理props和state同步的问题。取而代之的是,React推荐使用useEffect() hook或者componentDidUpdate()方法来处理这个问题。

需要注意的是,getDerivedStateFromProps方法是一个静态方法,不能访问this关键字,也不能调用组件的实例方法。如果需要访问组件的实例方法或属性,可以将它们作为props传递给组件。

测试如下代码3-1所示:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>1205_updating-生命周期(新)</title>
</head><body><div id="test"></div><!-- react核心库 --><script type="text/javascript" src="../js/17.0.1/react.development.js"></script><!-- 用于支持react操作DOM --><script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script><!-- 用于将jsx转为js --><script type="text/javascript" src="../js/17.0.1/babel.min.js"></script><script type="text/babel">/* 创建组件*  生命周期函数:生命周期钩子函数*/class Count extends React.Component {constructor(props) {console.log('Count === constructor');super(props)// 初始化状态this.state = {count: 1 // 计数}}/* +1*/add = () => {this.setState({count: this.state.count + 1})}/* 不更新状态,强制更新*/force = () => {console.log('Count ==== forceUpate');this.forceUpdate()}/* 卸载组件*/remove = () => {ReactDOM.unmountComponentAtNode(document.getElementById('test'))}/* 获取从props派生的state*/ static getDerivedStateFromProps(props, state) {console.log('Count ==== getDerivedStateFromProps');console.log(props, '----', state);return null// return props}/* 组件挂载完毕*/componentDidMount() {console.log('Count === componentDidMount');}/* 组件是否应该被更新*/shouldComponentUpdate(nextProps, nextState) {console.log('count === shouldComponentUpdate :', nextProps, '----', nextState);return true;}/* 组件将要被更新之后*/componentDidUpdate(prevProps, prevState) {console.log('count === componentDidUpdate :', prevProps, '----', prevState);}/* 组件卸载之前*/componentWillUnmount() {console.log('Count === componentWillUnmount');}/* 初始化渲染,状态更新重新渲染*/render() {console.log('Count === render');const { count } = this.statereturn (<div><h2>当前计数:{count}</h2><br /><button onClick={this.add}>点我+1</button><br/><button onClick={this.force}>不更改状态强制更新</button></div>)}}// 2.渲染虚拟DOM到页面ReactDOM.render(<Count count={99}/>, document.getElementById('test'))</script>
</body></html>

getDerivedStateFromProps 此方法 适用于及其罕见的案例,即state的值在任何时候都取决于props,目前在开发中很少使用。

4 getSnapshotBeforeUpdate

getSnapshotBeforeUpdate是React生命周期方法之一,在组件更新之前被调用,用于获取组件更新前的一些信息。该方法的返回值会作为第三个参数传递给componentDidUpdate()方法。

getSnapshotBeforeUpdate(prevProps, prevState)方法接收两个参数,prevProps表示组件更新前的props,prevState表示组件更新前的state。该方法必须返回一个值,用于在componentDidUpdate()方法中进行处理。

在React 16.x版本中,getSnapshotBeforeUpdate通常被用于获取组件更新前的某些信息,例如组件的滚动位置。获取到的信息可以在componentDidUpdate()方法中进行处理,例如恢复滚动位置等。

在React 17.x版本中,getSnapshotBeforeUpdate方法的行为与React 16.x版本相同,但是该方法不再推荐使用。取而代之的是,React推荐使用useEffect() hook或者componentDidUpdate()方法来处理获取组件更新前的信息。

需要注意的是,getSnapshotBeforeUpdate方法在组件挂载时不会被调用,只有在组件更新时才会被调用。同时,如果shouldComponentUpdate()方法返回false,getSnapshotBeforeUpdate()方法也不会被调用。

测试代码同上,加上getSnapshotBeforeUpdate方法,新增代码4-1如下所示:

/* 获取更新前的快照*/ getSnapshotBeforeUpdate(prevProps, prevState) {console.log('Count ==== getSnapshotBeforeUpdate');console.log(prevProps, '----', prevState);return '你好呀';
}/* 组件更新之后*/componentDidUpdate(prevProps, prevState, snapshot) {console.log('count === componentDidUpdate :', prevProps, '----', prevState);console.log('snapshot: ', snapshot);
}

效果如下图4-1所示

在这里插入图片描述

下面通过一个小案例,演示下该生命周期函数的应用场景。场景描述,又一个新闻列表,持续产生新的新闻,新闻出现在最上边。新闻盒子有固定高度,当新闻盒子满时,超出的旧新闻隐藏。我们希望实现当我们滚动到第几条新闻时,展示区域固定展示我们到的新闻,新新闻持续产生。

效果图示如下4-2所示:

在这里插入图片描述

实现代码如下4-2所示:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>1206_getSnapshotBeforeUpdate应用场景-生命周期()</title><style>.list{width: 200px;height: 150px;background-color: skyblue;overflow: auto;}.news{height: 30px;}</style>
</head><body><div id="news"></div><!-- react核心库 --><script type="text/javascript" src="../js/17.0.1/react.development.js"></script><!-- 用于支持react操作DOM --><script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script><!-- 用于将jsx转为js --><script type="text/javascript" src="../js/17.0.1/babel.min.js"></script><script type="text/babel">/* 创建组件*  生命周期函数:生命周期钩子函数*/class NewsList extends React.Component {state = {newsArr: []}componentDidMount() {setInterval(() => {// 获取原状态const {newsArr} = this.stateconst news = '新闻' + (newsArr.length + 1)// 更新状态this.setState({newsArr: [news, ...newsArr]})}, 1000);}getSnapshotBeforeUpdate() {// 获取内容高度return this.refs.list.scrollHeight}componentDidUpdate(prevProps, prevState, prevHeight) {// 滚动条持续滚动this.refs.list.scrollTop += this.refs.list.scrollHeight - prevHeight}/* 初始化渲染,状态更新重新渲染*/render() {return (<div className="list" ref='list'>{this.state.newsArr.map((n, index) => {return <div className="news" key={index}>{n}</div>})}</div>)}}// 2.渲染虚拟DOM到页面ReactDOM.render(<NewsList/>, document.getElementById('news'))</script>
</body></html>

后记

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/react-study

参考:

[1]React视频教程[CP/OL].2020-12-15.p43-p47.

[2]React官网[CP/OL].

[2]ChatGPT[CP/OL].