学习React的生命周期

203 阅读3分钟

旧版

执行顺序

  • contructor中进行初始化,设置props和state

  • 组件挂载阶段

    1. componentWillMount():组件将要挂载 componentWillMount在渲染过程中可能会执行多次
    2. render : 组件进行渲染,props或者state改变,则会重新执行
    3. componentDidMount() :组件完成挂载 只执行一次,一般是在componentDidMount执行副作用,进行异步操作
  • 组件更新阶段

    1. shouldComponentUpdate(nextProps,nextState) :组件是否更新,如果返回值为false,则不进行渲染,不向下执行【不能在此调用setState,否则会造成死循环】
    2. shouldComponentUpdate返回值为true,向下执行
    3. componentWillUpdate:组件将要更新
    4. render 组件进行渲染(如果有子组件
    5. 子组件 componentWillReceiveProps:组件收到新的属性对象 【第一次不执行,当props发生改变时才会触发】
    6. 子组件 shouldComponentUpdate(假定返回值为true)
    7. 子组件 componentWillUpdate:组件将要更新
    8. 子组件 componentDidUpdate:组件更新完成 9 父组件 componetDidUpdate: 组件更新完成
  • 组件卸载

    1. componentWillUnmount

PureComponent

  • 减少组件的渲染
  • 只适合类组件(React.PureComponent)
  • 在shouldComponentUpdate的阶段进行判断状态是否改变,从而决定组件是否更新
  • 代码实现
import React from 'react'
export class PureComponent extends React.Component {
	isPureComponent = true
	//传入新的属性对象和状态对象,然后返回一个是否需要更新的boolean值
	shouldComponentUpdate(nextProps, nextState) {
		return (
			!shallowEqual(this.props, nextProps) ||
			!shallowEqual(this.state, nextState)
		)
	}
}

//浅比较 比较obj1和obj2是否相等,如果相等的话则返回true,不相等返回false,只比较第一层
function shallowEqual(obj1, obj2) {
	if (obj1 === obj2) {
		return true
	}

	if (
		typeof obj1 !== 'object' ||
		obj1 === null ||
		typeof obj2 !== 'object' ||
		obj2 === null
	) {
		return false
	}

	let keys1 = Object.keys(obj1)
	let keys2 = Object.keys(obj2)
	if (keys1.length !== keys2.length) {
		return false
	}

	for (let key of keys1) {
		if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
			return false
		}
	}
	return true
}

  • 函数组件(React.memo)
    • 入参是个需要根据状态是否改变决定是否重新渲染的函数组件
    • 返回值一个继承PureComponent的类组件,渲染的就是传入的函数组件
    • 代码实现
    import React from 'react'
    export default function memo(FunComponent) {
    	return class extends React.PureComponent {
    		render() {
    			// return FunComponent(this.props) 可行
    			return <FunComponent {...this.props} />
    		}
    	}
    }
    
    

新版(react16以后)

新版和旧版的区别

  • 删除的方法(但是依旧可以使用,只是不建议使用 )

    • componentWillMount
    • componentWillUpdate
    • componentWillReceiveProps
  • 增加的方法

    • getDerivedStateFromProps
        static getDerivedStateFromProps(props,state)
        1. 将传入的props映射到state上
        2. 在初始化之后执行,可执行多次
        3. 第一次接收到props也会执行,不会等到第二次更新之后才执行
        4. 只要组件重新渲染 就会执行
    
    • getSnapshotBeforeUpdate
        1. render 之前 componentDidUpdate 之后
        2. getSnapshotBeforeUpdated的返回值会做为第三个参数传给componentDidUpdate
        3. 使用的时机是:可以读取DOM,但是不可以操作DOM
    
    
    • 案列:滚动条固定在某一位置上,当前内容区显示的信息,不改变
import React from 'react'


export default class LifeCycleNew extends React.Component {
	constructor(props) {
		super(props)
		this.inputValue = React.createRef()
		this.wrapper = React.createRef()
		this.state = {
			num: 1,
			msg: []
		}
		console.log('1.  组件初始化 initialization: 设置props和state')
	}

	addMsg = () => {
		this.setState({
			msg: [this.state.msg.length, ...this.state.msg]
		})
	}

	componentDidMount() {
		console.log('4.  组件挂载完成 componentDidMount')
		this.timeID = setInterval(() => {
			this.addMsg()
		}, 1000)
	}

	shouldComponentUpdate(nextProps, nextState) {
		console.log('5.  组件是否更新 shouldComponentUpdate')
		return nextState.num % 3
	}

	componentDidUpdate(preProps, preState, preScrollHeight) {
		console.log('7.  组件更新完成 componentDidUpdate')
		this.wrapper.current.scrollTop =
			this.wrapper.current.scrollTop +
			(this.wrapper.current.scrollHeight - preScrollHeight)
	}

	getSnapshotBeforeUpdate() {
		console.log('8.  getSnapshotBeforeUpdate')
		// 返回当前元素未更新前的内容高度
		return this.wrapper.current.scrollHeight
	}

	componentWillUnmount() {
		window.clearInterval(this.timeID)
	}



	render() {
		console.log('3.  组件进行渲染 render')
		return (
			<div
				ref={this.wrapper}
				style={{
					border: '5px solid red',
					padding: '10px',
					height: '200px',
					overflow: 'auto'
				}}
			>
				{this.state.msg.map((item, index) => (
					<div key={index}>{item} </div>
				))}
			
				
			
			</div>
		)
	}
}


错误边界(Error Boundaries)

  • 解决错误问题:

    • 如果当一个组件异步加载下载js文件时,网络错误,无法下载js文件
    • 使用Suspense时,没有fallback属性
  • 解决方法:

    • 声明周期componentDidCatch来处理错误 (打印错误信息)
    • 静态方法 static getDerivedStateFromError 来处理错误 (渲染备用 UI )