React组件的生命周期深入理解分析

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

React组件的生命周期深入理解分析

花铛   2022-12-01 我要评论

组件从创建到销毁的过程,被称为组件的生命周期。

在生命周期的各个阶段都有相对应的钩子函数,会在特定的时机被调用,被称为组件的生命周期钩子。

生命周期回调函数 = 生命周期钩子函数 = 生命周期函数 = 生命周期钩子

函数式组件没有生命周期,因为生命周期函数是 React.Component 类的方法实现的,函数式组件没有继承 React.Component,所以也就没有生命周期。

<-- 容器!-->
<div id="test"></div>
// 创建组件
class Life extends React.Component{
	state = {opacity:1}
	// 调用时机:组件挂载完毕
	componentDidMount(){
		this.timer = setInterval(() => {
			let {opacity} = this.state
			opacity -= 0.1
			if(opacity <= 0) opacity = 1
			this.setState({opacity})
		}, 200);
	}
	//调用时机:组件将要卸载
	componentWillUnmount(){
		clearInterval(this.timer)
	}
	handleUnmount = ()=>{
		//卸载组件
		ReactDOM.unmountComponentAtNode(document.getElementById('test'))
	}
	//调用时机:初始化渲染、状态更新之后
	render(){
		return(
			<div>
				<h2 style={{opacity:this.state.opacity}}>我是一段透明度会变化的文字</h2>
				<button onClick={this.handleUnmount}>点击卸载</button>
			</div>
		)
	}
}
//渲染组件
ReactDOM.render(<Life/>,document.getElementById('test'))

生命周期钩子(新)

新的生命周期钩子增加了 getDerivedStateFromProps 和 getSnapshotBeforeUpdate。

constructor():constructor() 构造函数在 React 组件挂载之前被调用。

如果不初始化 state 或不为事件处理函数绑定实例,则不需要写 constructor()。

不能在 constructor() 构造函数内部调用 this.setState(), 因为此时第一次 render() 还未执行,也就意味 DOM 节点还未挂载。

static getDerivedStateFromProps(nextProps, prevState):在每次调用 render() 方法之前都会被调用,在初始化和更新时都会被调用。

getDerivedStateFromProps() 第一个参数为即将更新的 props,第二个参数为上一个状态的 state,可以比较 props 和 state 来加一些限制条件,防止无用的 state 更新。

getDerivedStateFromProps() 的返回值是必须的。返回一个对象来更新 state,如果不需要更新,返回 null 即可。 getDerivedStateFromProps() 适用于 state 的值在任何时候都取决于 props 的情况。 getDerivedStateFromProps() 是一个静态函数,是放在组件身上的,而不是放在组件实例身上,因此不能使用 this。

// 之前使用 componentWillReceiveProps
componentWillReceiveProps(nextProps) {
    if (nextProps.location.search !== this.props.location.search) {
    	this.init()
    }
}
// 现在使用 getDerivedStateFromProps:相当于把 componentWillReceiveProps 拆分成 getDerivedStateFromProps 和 componentDidUpdate
static getDerivedStateFromProps(nextProps, prevState) {
  const {search} = nextProps.location
  if (search !== prevState.search) {
    return {
      search,
    }
  }
  return null
}
componentDidUpdate(prevProps, prevState) {
  const {search} = this.state
   if (search !== prevState.search) {
    this.init()
  }
}

render():render() 方法是类组件中唯一必须实现的方法,用于渲染 DOM,render() 方法必须返回 reactDOM。

在 render() 的 return 之前不能写 setState,否则会触发死循环导致内存崩溃;return 体里面是可以写的。

// Wrong
render(){
	this.setState({...})
	return (...)
}
// Correct
render(){
	return (
		<input onClick={()=>this.setState({...})} />
   )
}

componentDidMount():在组件挂载后 (插入 DOM 树后) 立即调用,此生命周期是发送网络请求、开启定时器、订阅消息等的好时机,并且可以在此钩子函数里直接调用 setState()。

shouldComponentUpdate(nextProps, nextState):在组件更新之前调用,可以控制组件是否进行更新, 返回 true 时组件更新, 返回 false 则不更新。不写此生命周期钩子时默认为 true。

shouldComponentUpdate() 第一个参数是即将更新的 props 值,第二个参数是即将更新后的 state 值,可以根据更新前后的 props 或 state 来比较加一些限制条件,决定是否更新,进行性能优化。

不要在 shouldComponentUpdate 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃。

getSnapshotBeforeUpdate(prevProps, prevState):在最新的渲染数据提交给 DOM 前会调用,也就是说,在 render 之后,在 componentDidUpdate 之前调用。使得组件可以在更新之前获取快照值。不常用。

它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置),此生命周期返回的任何值都会作为参数传递给 componentDidUpdate(),如不需要传递任何值,那么返回 null。返回值是必须的。

componentDidUpdate(prevProps, prevState, snapshot):componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行。

包含三个参数,第一个是上一次props值。 第二个是上一次state值,第三个是“snapshot” 参数传递。

可以进行前后 props 的比较进行条件语句的限制,来进行 setState() , 否则会导致死循环。

componentWillUnmount():componentWillUnmount() 在组件即将被卸载或销毁时进行调用。此生命周期是清理定时器、取消订阅等操作的好时机。

组件的挂载流程:

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount

setState 更新流程:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate

组件的卸载流程: componentWillUnmount

生命周期钩子(旧)

React 从 v16.3 开始废弃 componentWillMount、componentWillReceiveProps、componentWillUpdate 三个钩子函数。在新版本中使用需要加上 UNSAFE_ 前缀,否则会触发控制台的警告。

UNSAFE 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 Bug,尤其是在启用异步渲染之后。

组件的挂载流程:

  • constructor
  • componentWillMount
  • render
  • componentDidMount

setState 更新流程:

  • shouldComponentUpdate
  • componentWillUpdate(组件更新之前调用)
  • render
  • componentDidUpdate

forceUpdate 强制更新流程:

  • componentWillUpdate(组件更新之前调用)
  • render
  • componentDidUpdate

父组件 render 之后子组件的更新流程:

  • componentWillReceiveProps(子组件接收到新的 props 之前调用,第一次接收到 props 不会调用)
componentWillReceiveProps(nextProps) {
	// 可以和 this.props 中的数据进行对比,以决定是否要执行某些方法
}
  • shouldComponentUpdate
  • componentWillUpdate(组件更新之前调用)
  • render
  • componentDidUpdate

组件的卸载流程:

componentWillUnmount

父子组件生命周期

当子组件自身的 state 状态改变,不会对父组件产生副作用的情况下,父组件不会进行更新,也就是不会触发父组件的生命周期。

当父组件状态变化时(不会是否更改到传给子组件的 props),会触发自身和子组件对应的生命周期。

render 以及 render 之前的生命周期,父组件先执行;

render 之后的生命周期,子组件先执行,并且是与父组件交替执行。

父子组件初始化流程:

  • 父组件constructor
  • 父组件getDerivedStateFromProps
  • 父组件render
  • 子组件constructor
  • 子组件getDerivedStateFromProps
  • 子组件render
  • 子组件componentDidMount
  • 父组件componentDidMount

子组件修改自身的 state 状态流程:

  • 子组件getDerivedStateFromProps
  • 子组件shouldComponentUpdate
  • 子组件render
  • 子组件getSnapshotBeforeUpdate
  • 子组件componentDidUpdate

父组件修改 state 状态流程:

  • 父组件getDerivedStateFromProps
  • 父组件shouldComponentUpdate
  • 父组件render
  • 子组件getDerivedStateFromProps
  • 子组件shouldComponentUpdate
  • 子组件render
  • 子组件getSnapshotBeforeUpdate
  • 父组件getSnapshotBeforeUpdate
  • 子组件componentDidUpdate
  • 父组件componentDidUpdate

父组件卸载子组件:

// 通过点击父组件中的 [卸载 / 挂载子组件] 按钮来卸载子组件
handelToggle = () => {
    this.setState({
      isHidden: !this.state.isHidden
    })
  }
<button onClick={this.handelToggle}>挂载/卸载子组件</button>
{this.state.isHidden ? '' : <Child />>}
  • 父组件getDerivedStateFromProps
  • 父组件shouldComponentUpdate
  • 父组件render
  • 父组件getSnapshotBeforeUpdate
  • 子组件componentWillUnmount
  • 组件componentDidUpdate

父组件重新挂载子组件:

再次点击父组件中的 [卸载 / 挂载子组件] 按钮来挂载子组件。

  • 父组件getDerivedStateFromProps
  • 父组件shouldComponentUpdate
  • 父组件render
  • 子组件constructor
  • 子组件getDerivedStateFromProps
  • 子组件render
  • 父组件getSnapshotBeforeUpdate
  • 子组件componentDidMount
  • 父组件componentDidUpdate

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们