React中组件的生命周期

1,251 阅读6分钟

组件的生命周期

今天学习了组件的生命周期~

理解

  • 生命周期的回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
  1. 组件从创建到死亡经历的一些特定的阶段
  2. React组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用
  3. 我们在定义组件的时候,会在特定的生命周期回调函数中做特定的工作。

1 .组件的挂载和卸载

首先讲一下出生与死亡——挂载与卸载。

(1)当 Clock 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)”。(2)同时,当 DOM 中 Clock 组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)”。在组件挂载完毕的时候Life的实例对象会调用componentDidMount()这个函数,而在组件将要卸载的时候会调用componentWillunmount()函数。

接下来通过这个demo展示一下挂载和卸载组件的应用:

请添加图片描述

这是一个通过类式组件实现的demo,一进入页面的时候上面的文字就会发生透明度会一直发生改变(挂载组件),当点击“你好”按钮的时候,上面的文字就会消失(卸载组件

  1. 首先,注意定时器的位置:先创建一个组件,通过改变状态stateopacity,在每一次改变state的值的时候都会调用一次render的函数。此时如果将定时器放在render里面的话是会导致透明度的变化越来越快(定时器叠加),此时我们就需要使用挂载完毕之后调用的函数componentDidMount()
  2. 接下来给按钮添加功能,也就是让它在点击的时候调用death这个函数来卸载这个组件,在这之前,我们应该把定时器先清除了(组件不存在了,那么定时器也没有存在的必要了)。此时我们可以调用componentWillunmount函数(这个函数会在组件将要卸载的时候调用),或者把清除定时器的语句放在ReactDOM.unmountComponentAtNode之前也是可以的。
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);
    }
    death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('test'));
    }

ReactDOM.unmountComponentAtNode用来卸载在某个结点上的组件。通过这个小demo可以很好的理解挂载和卸载的实际应用。生命周期函数的调用很写的时候它们的顺序无关。

接下来就进入生命周期的学习啦!

2. 生命周期(旧)请添加图片描述

这张图的思路很清晰。需要注意的是,我们前面说过每一次setState的时候会调用一次render,其实不只是rendershouldComponentUpdatecomponentWillUpdate也会再次被调用。shouldComponentUpdate决定了组件是否应该被更新,默认返回true,如果手动去把它的返回值更改为false,那么阀门被关闭,组件不会被更新。但如果调用的是强制更新函数forceUpdate(),不会受到shouldComponentUpdate返回值(阀门)的影响。

父组件render

下面这段代码的父组件就是A,子组件是B。通过在父组件的render的返回值中添加<B str={this.state.str} />来实现对B的渲染。子组件会受到父组件的影响(传值)。

class A extends React.Component {
    state = { str: 'A' };
    changeStr = () => {
        this.setState({ str: 'B' })
    }
    render() {
        return (
            <div>
                <div>A组件(父)</div>
                <button onClick={this.changeStr}>change</button>
                <B str={this.state.str} />
            </div>
        )
    }
}
class B extends React.Component {
    componentWillReceiveProps(props) {
        console.log('B_componentWillReceiveProps', props);
    }
    render() {
        return (
            <div>B组件(子):{this.props.str} </div>
        )
    }
}
ReactDOM.render(<A />, document.getElementById('test'));

实现效果:

请添加图片描述

componentWillReceiveProps在刚开始进入页面的时候是不调用的,点击按钮后(也就是父组件再次调用render),<B str={this.state.str} />这句语句重新执行,才会调用componentWillReceiveProps

总结生命周期(旧)

生命周期的三个阶段

(1) 初始化阶段: 由ReactDOM.render()触发---初次渲染

  1. constructor()

  2. componentWillMount()

  3. render()

  4. componentDidMount() ---常用

    一般在这个钩子中初始化。例如:开启定时器、发送网络请求u、订阅消息等。

(2) 更新阶段: 由组件内部this.setSate()或父组件重新render触发

  1. shouldComponentUpdate() (强制更新其实就是少了这个环节
  2. componentWillUpdate()
  3. render() ---必用
  4. componentDidUpdate()

(3) 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 1. componentWillUnmount() ---常用一般1在这个钩子中做收尾工作。例如:关闭定时器、取消订阅消息等。

3. 生命周期(新)

新旧版本生命周期的区别

新版本与旧版本生命周期不同的是,(即将)启用了componentWillMountcomponentWillReceivePropscomponentWillUpDate这三个函数,如果要使用的话,需要在前缀添加UNSAFE。并且,对于新版本的生命周期,多了两个函数,在挂载和更新时多了一个getDerivedStateFromProps函数,在rendercomponentDidUpdate之间多了一个getSnapshotBeforeUpdate函数。

请添加图片描述

在react官方文档中我们可以看到下面这段话。如果我们使用的是新版本的依赖包,在下面这几个函数没有添加UNSAFE前缀的话,那么将会出现waring警告。

React官方.png

接下来说新版本出现的2个新函数:

  • getDerivedStateFromProps()(基本不用): 得到一个派生的状态。需要用static来定义,需要返回一个状态对象state objnull;能够接收到一个参数props并且返回它。如果调用这个函数的话,state的值在任何时候都取决于props,无论初始化或者修改都无法改变。使用场景:state在任何时候都取决于props

但是,派生状态会导致代码冗余,并使组件难以维护

static getDerivedStateFromProps(props, state) {
    console.log(props, state);  // 这个state使初始化状态的属性和属性值
    return props;
}
ReactDOM.render(<Count count="100" />, document.getElementById('test'));
  • getSnapshotBeforeUpdate()componentDidUpdate配合使用,在最近一次渲染输出之前调用,使得组件能在发生更改之前从DOM中捕获一些信息(例如:滚动位置)。此生命周期的任何返回值都将作为参数传递给componentDidUpdate()

    // 在页面出现效果之前存个副本
    getSnapshotBeforeUpdate() {
        console.log("getSnapshotBeforeUpdate");
        return 'mannqo'
    }
    componentDidUpdate(preProps, preState, snapshowValue) {
        console.log('componentDidUpdate', preProps, preState, snapshowValue);
    }
    

    应用场景:内容不断更新渲染,当你获得它在某一时刻的渲染结果。(比如不断渲染新的li,当溢出的时候显示滚动条,此时如果你滚动到某一个li之后,由于不断渲染着新的li,滚动条会自动发生滚动,此时如果你想让滚动条不滚动,那么就要用到getSnapshotBeforeUpdate()

总结

重要的钩子:

  1. render: 初始化渲染或更新渲染调用
  2. componentDidMount: 开始监听,发送ajax请求
  3. componentWillUnmount: 做一些收尾工作,如:清除定时器

今天的学习就到这里啦!明天继续加油~