美文网首页
React 的生命周期

React 的生命周期

作者: alokka | 来源:发表于2019-05-27 14:03 被阅读0次

    React v16.0前的生命周期

    其实大部分团队不见得会跟进升到16版本,所以16前的生命周期还是很有必要掌握的,何况16也是基于之前的修改

    5caca39f0001b0a619201080.jpg

    生命周期可分为四个阶段:

    1. 组件初始化(initialization)阶段:

    - constructor 方法
    1. 组件一创建就会调用 es6 class类中的 constructor 方法(最先执行)
    2. super(props)`用来调用类的构造方法( constructor() ), 也将父组件的props注入给子组件,子组件中props只读不可变,state可变
    3. constructor()用来做一些组件的初始化工作,如定义this.state的初始内容
    class TodoList extends Component {
        // 组件一创建最先执行,早于render
        constructor(props) {
            super(props);
            this.state = {
                inputValue: '',
                list: []
            }
        }
    }
    

    2. 组件的挂载(Mounting)阶段(已插入真实 DOM)

    - componentWillMount 方法

    在组件挂载到DOM前调用,且只会被调用一次,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中,所以项目中很少用

    - render 方法

    根据组件的props和state(两者的重传递和重赋值,无论值是否有变化,都可以引起组件重新render) ,当父组件的 render 函数被运行时, 它的子组件的 render 函数也会被重新运行

    return 一个React元素(描述组件,即JSX模板),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。

    - componentDidMount 方法

    组件挂载到DOM后调用,且只会被调用一次

    3. 组件的更新(Updating)阶段

    - componentWillReceiveProps 方法(nextProps)

    (只跟父组件传过来的 props 的变化有关,所以只有能接受 props 参数的子组件才有这个方法)

    此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props

    而且这个组件第一次存在于父组件的是时候不会被执行(就是第一次不会执行,以后如果 nextProps 参数有变化才会被执行

    class Child extends Component {
        constructor(props) {
            super(props);
            this.state = {
                someThings: props.someThings
            };
        }
        componentWillReceiveProps(nextProps) { // 父组件重传props时就会调用这个方法
            this.setState({someThings: nextProps.someThings});
        }
        render() {
            return <div>{this.state.someThings}</div>
        }
    }
    
    - shouldComponentUpdate(nextProps, nextState) 方法

    此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能。

       shouldComponentUpdate(nextStates){ // 应该使用这个方法,否则无论state是否有变化都将会导致组件重新渲染
            if(nextStates.someThings === this.state.someThings){
              return false
            }
        }
    
    - componentWillUpdate(nextProps, nextState) 方法

    此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用

    - render 方法

    render方法在上文讲过,这边只是重新调用

    - componentDidUpdate(prevProps, prevState)

    此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state

    4. 卸载阶段

    - componentWillUnmount 方法

    此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。

    完整的生命周期在 React 组件中的应用

    class TodoList extends Component {
    
      constructor(props) {
        console.log('constructor');
        super(props);
        this.state = {
          list: [],
          inputValue: ''
        }
      }
    
      // 组件加载前
      componentWillMount() {
        console.log('componentWillMount');
      }
    
      // nextProps 参数变化后执行 第一次不执行
      componentWillReceiveProps(nextProps) { // 只有子组件有
        console.log('componentWillReceiveProps');
      }
    
      // shouldComponentUpdate 在组件更新前比较参数和数据,自己返回布尔值,优化组件性能 
      shouldComponentUpdate(nextStates){
        console.log('shouldComponentUpdate');
        return true;
        // if(nextStates.someThings === this.state.someThings){
        //   return false
        // }
    }
    
      // componentWillUpdate 在 render 前执行
      componentWillUpdate() {
        console.log('componentWillUpdate');
      }
    
      // 生成描述文件 和 渲染
      render() {
        console.log('render');
        return (
          <Fragment>
            <div className="App">
              <div className="App-header">
                <img src={logo} className="App-logo" alt="logo" />
              </div>
              <div>
              <input className='target' value={this.state.inputValue} onChange={this.handleInputChange} />
              <button className='red-btn' onClick={this.handleBtnClick}>add</button>
              </div>
              <ul>
                {
                  this.getTodoItems()
                }
              </ul>
            </div>
          </Fragment>
        );
      }
    
      // 组件加载后
      componentDidMount() {
        console.log('componentDidMount');
      }
    
      // componentDidUpdate 在组件更新后被调用
      componentDidUpdate() {
        console.log('componentDidUpdate');
      }
    
    
      // componentWillUnmount 在组件被卸载前调用
      componentWillUnmount() {
        console.log('componentWillUnmount');
      }
    }
    

    React v16.4 的生命周期

    1558239253(1).jpg

    新引入了两个新的生命周期函数

    - getDerivedStateFromProps 方法

    在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容

    - getSnapshotBeforeUpdate 方法

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

    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>
        );
      }
    }
    

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

    相关文章

      网友评论

          本文标题:React 的生命周期

          本文链接:https://www.haomeiwen.com/subject/jgafzqtx.html