loading

react基础(二)

react生命周期 搞起来

# react组件的生命周期

# react 17生命周期的变化

起先对着网上博客文章一顿看,然后动手敲起来,才发现有的生命周期在vscode中的代码提示里已经被置灰了。

去官网看了一下文档才发现,原来这两年react大版本已经更新到v17了,生命周期已经和几年前的那些博客中所提的有了较大改变。所以说学习新知识最好的途径还是官方文档,其次才是别人的经验。

然后先附上两张图,分别对应之前版本和此刻v17版本的react周期的变化,或许未来又会有不同。

老版本生命周期:

v17中版本的生命周期:

从图中首先可以看出新版本中

  • 剔除了三个生命周期componentWillMountcomponentWillUpdatecompontentWillReceiveProps
  • 新加入了两个生命周期静态的getDerivedStateFromPropsgetSnapshotBeforeUpdate

其实在react 17版本中剔除的这三个生命周期并不是从react中删除了,我敲了一下发现它们还是存在的,只是官方不在建议使用他们。剔除原因,移步官网 (opens new window)

# 挂载阶段

# constructor()

在react组件挂载之前,会调用它的构造函数。在为React.Component子类实现构造函数时,应在其他语句之前前调用super(props)。否则, this.props在构造函数中可能会出现未定义的bug。

通常,在 React 中,构造函数仅用于以下两种情况:

  • 通过this.state赋值对象来初始化内部state,在constructor()函数中不要调用setState()方法。
  • 事件处理函数绑定实例。

# componentDidMount()

componentDidMount()会在组件挂载后(插入DOM树中)立即调用。依赖于DOM节点的初始化应该放在这里。请求后台接口的操作可以放到这个生命周期内执行。在这个生命周期内同时也适合添加订阅,但要记得在componentWillUnmount()周期内取消订阅。

在这个生命周期内可以使用setState()方法来改变组件的state的属性值,它将触发额外渲染,但它发生在浏览器更新屏幕前,因此用户不会看出中间状态。

通过这两个生命周期,我们就可以实现从后台接收数据渲染到页面的操作:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { queryLabelInfo } from '../api/test'

class List extends Component {
  constructor() {
    super();
    this.state = {
      list: []
    };
  };
  componentDidMount() {
    // 请求后台接口
    queryLabelInfo({ current: 1, size: 10})
    .then(res => {
      this.setState({
        list: res.data.records
      })
    })
  };
  render() {
    return (
      <div>
        <div>
          {
            this.state.list.map(item => {
                return <div key={item.id}>{item.typeName}</div>
            })
          }
        </div>
      </div>
    )
  }
}
ReactDOM.render(
    <List />,
    document.getElementById('root')
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

效果如图:

# static getDerivedStateFromProps()

getDerivedStateFromProps会在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新state,如果返回null则不更新任何内容。

这个生命周期中无权访问组件实例。如果需要,可以通过提取组件props的纯函数及class之外的状态,在getDerivedStateFromProps()和其他class方法之间重用代码。

# 更新阶段

# shouldComponentUpdate()

根据shouldComponentUpdate()的返回值,判断React组件的输出是否受当前state或props更改的影响。默认行为是state每次发生变化组件都会重新渲染。

当props或state发生变化时shouldComponentUpdate()会在渲染执行之前被调用。返回true则更新视图,返回false则不更新视图。返回值默认为true。首次渲染或使用forceUpdate()方法时不会调用该方法。

案例:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Counter extends Component {
    constructor() {
        super();
        this.state = {
          count: 0,
        };
    };
    //返回false视图不在更新 ture则更新
    shouldComponentUpdate(newProps, newState) {
        if(newState.count > 3) {
            return false
        }
        return true
    };
    add() {
        this.setState({
            count: this.state.count + 1
        })
        console.log('count已增加,值为:', this.state.count)
    };
    render() {
        return (
            <div>
                <span>{this.state.count}</span>
                <button onClick={this.add.bind(this)}>+</button>
            </div>
        )
    };
    ReactDOM.render(
        <Counter />,
        document.getElementById('root')
    );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate()在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。返回值应为snapshot的值(或null

这个暂时不知道有什么使用场景,官网示例:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我们是否在 list 中添加新的 items ?
    // 捕获滚动​​位置以便我们稍后调整滚动位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
    // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
    //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

在上述示例中,重点是从 getSnapshotBeforeUpdate 读取 scrollHeight 属性,因为 “render” 阶段生命周期(如 render)和 “commit” 阶段生命周期(如 getSnapshotBeforeUpdate 和 componentDidUpdate)之间可能存在延迟。

# componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)会在更新后立刻被执行,但首次渲染不会执行此方法。

这里可以对比更新前后的propsstate,然后进行相应操作,如网络请求或者调用setState方法。但要注意一定要对比变化,放在条件语句中中执行,否则可能会反复执行导致死循环

  • 最好不要此生命周期内调用setState
  • 第一次初始化组件时,此方法不会执行;
  • shouldComponentUpdate返回false时此方法同样不会执行。

# 卸载阶段

# componentWillUnmount()

componentWillUnmount()会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除timer,取消网络请求或清除在componentDidMount()中创建的订阅等。

componentWillUnmount()中不应调用setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

# componentDidCatch(error, info)

当组件发生异常时会被调用的钩子。 它接收两个参数:

  • error —— 抛出的错误。
  • info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。

注意点:

  • 只能在父级组件捕获子组件的异常;
  • 如果异常被 try...catch 包裹父级组件的钩子就不会执行了。

# 小结

今天学习了react生命周期,联系vue

  • constructor相当于vue的created,组件还未挂载,dom元素还不可访问,可以进行一些数据处理;
  • componentDidMount相当于vue的mounted,组件完成挂载,可以对dom元素进行操作;
  • componentDidUpdate相当于vue的updated,由于数据更新导致重新渲染时被调用的钩子;
  • componentWillUnmount相当于vue的beforeDestroy,组件从dom中移除时会触发的钩子;
  • getDerivedStateFromPropsshouldComponentUpdategetSnapshotBeforeUpdate似乎在vue中没有对照。
最近更新时间: 2021/08/13 15:27:23
最近更新
01
2021/10/23 00:00:00
02
2021/08/18 17:00:03
03
2021/07/04 15:26:36