React 生命周期上海时时乐走势图官网

总结

如果你写的工程需要同时兼容React及Preact的话:

  • 不要利用React下 setState 在同一次组件更新执行前 state 不立即更新的特性,注意多个 setState 之间是否影响,必要时手动保存旧值。
  • 在组件更新生命周期内,除 componentWillReceiveProps 之外不要使用 setState,提供了 nextState 的生命周期,可以直接修改 nextState。
  • 尽量避免使用 setState 函数修改方式,在 componentWillReceiveProps 中使用时,使用生命周期中的 prevProps(this.props) 和 nextProps。

p.s: antd-mobile 2.0正式版已发布,同时兼容react、preact,轻量、快速、易用的移动端组件库,等你来用~ 【传送门】

挂载: componentDidMount

  1. 在初始化渲染执行之后立刻调用一次,仅客户端有效(服务器端不会调用)
  2. 在生命周期中的这个时间点,组件拥有一个 DOM 展现,你可以通过 this.getDOMNode() 来获取相应 DOM 节点
  3. 发送 AJAX 请求,可以在该方法中执行这些操作
  4. 例子

    React.createClass({
      componentDidMount() {
        ...
      }
    });
    

     

更新阶段:

export function renderComponent(component, opts, mountAll, isChild) {
  ...
  previousProps = component.prevProps || props,
  previousState = component.prevState || state,
  previousContext = component.prevContext || context,
  ...

  // if updating
  if (isUpdate) {
    component.props = previousProps;
    component.state = previousState;
    component.context = previousContext;
    if (opts!==FORCE_RENDER
      && component.shouldComponentUpdate
      && component.shouldComponentUpdate(props, state, context) === false) {
      skip = true;
    }
    else if (component.componentWillUpdate) {
      component.componentWillUpdate(props, state, context);
    }
    component.props = props;
    component.state = state;
    component.context = context;
  }
  ...
}

在更新流程前提取了旧 state,shouldComponentUpdate、componentWillUpdate 之后还原回新值,所以在 shouldComponentUpdate 生命周期中,this.props 将获取的是 prevProps,这里与 React 的逻辑并不一致。

2. 当用户从 '/' 跳转到 '/invoice/123'

  • App 从 router 中接收到新的 props(例如 childrenparamslocation 等数据), 所以 App 触发了 componentWillReceiveProps 和 componentDidUpdate 两个生命周期方法
  • Home 不再被渲染,所以它将被移除,触发 componentWillUnmount
  • Invoice 首次被挂载 触发 componentWillMount 和 componentDidMount

上海时时乐走势图官网 1

 

setState 阶段:

// ReactUpdateQueue.js
enqueueSetState: function(publicInstance, partialState) {
  ...

  var queue =
    internalInstance._pendingStateQueue ||
    (internalInstance._pendingStateQueue = []);
  queue.push(partialState);

  enqueueUpdate(internalInstance);
}

可以看到React在 setState 的时候不会做任何处理,会把变更直接放到一个专门处理 state 的队列里供组件更新时使用。

1. 当用户打开应用的 '/' 页面

上海时时乐走势图官网 2

 

源码分析

首先来分析下React以及Preact在setState部分的具体实现。

(太长不看想偷懒,可以直接下翻看结论)

挂载:componentWillMount

 

  1. 服务器端和客户端都只调用一次,在初始化渲染执行之前立刻调用。
  2. 如果在这个方法内调用 setStaterender() 将会感知到更新后的 state
  3. 用法

    React.createClass({
      componentWillMount() {
        ...
      }
    });
    

     

更新阶段:

// ReactCompositeComponent.js
updateComponent: function(
  transaction,
  prevParentElement,
  nextParentElement,
  prevUnmaskedContext,
  nextUnmaskedContext,
) {
  var inst = this._instance;
  ...

  var willReceive = false;
  var nextContext;

  if (this._context === nextUnmaskedContext) {
    nextContext = inst.context;
  } else {
    nextContext = this._processContext(nextUnmaskedContext);
    willReceive = true;
  }

  var prevProps = prevParentElement.props;
  var nextProps = nextParentElement.props;

  if (prevParentElement !== nextParentElement) {
    willReceive = true;
  }

  if (willReceive && inst.componentWillReceiveProps) {
    ...
    inst.componentWillReceiveProps(nextProps, nextContext);
  }

  // 在此处才计算 nextState
  var nextState = this._processPendingState(nextProps, nextContext); // 此处传入了 nextProps
  var shouldUpdate = true;

  if (!this._pendingForceUpdate) {
    if (inst.shouldComponentUpdate) {
      ...
      shouldUpdate = inst.shouldComponentUpdate(
        nextProps,
        nextState,
        nextContext,
      );
    } else {
      if (this._compositeType === CompositeTypes.PureClass) { // 敲黑板,知识点 —— 如果你的组件没实现shouldComponentUpdate,那么把React.Component 换成 React.PureComponent 可以获得基础版优化,提高性能。
        shouldUpdate =
          !shallowEqual(prevProps, nextProps) ||
          !shallowEqual(inst.state, nextState); // 浅比较,可以抄去自己改成属性黑/白名单版
      }
    }
  }
  ...
}

// ReactCompositeComponent.js
_processPendingState: function(props, context) { // props: nextProps
  var inst = this._instance;
  var queue = this._pendingStateQueue;
  var replace = this._pendingReplaceState;
  this._pendingReplaceState = false;
  this._pendingStateQueue = null;

  if (!queue) {
    return inst.state;
  }

  if (replace && queue.length === 1) {
    return queue[0];
  }

  var nextState = Object.assign({}, replace ? queue[0] : inst.state);
  for (var i = replace ? 1 : 0; i < queue.length; i  ) {
    var partial = queue[i];
    Object.assign(
      nextState,
      typeof partial === 'function'
        ? partial.call(inst, nextState, props, context) // nextProps
        : partial,
    );
  }

  return nextState;
}

通过上面组件更新的流程代码可以看到:

  • 在 updateComponent 中,在 componentWillReceiveProps 之后才会计算 nextState,所以在 componentWillReceiveProps 中 setState 是可以在当次更新中生效的。
  • 在 _processPendingState 会对队列里的 state 进行叠加,如果修改是函数方式,此处传入的state参数是 nextState,props 是 nextProps。

4. 当从 /invoice/789 跳转到 /accounts/123

 上海时时乐走势图官网 3

 

Preact是React的轻量级实现,是React比较好的替代者之一,有着体积小的优点,当然与React之间一定会存在实现上的差异,本文介绍了在 setState 方面的差异之处。

更新: shouldComponentUpdate

  1. 返回:boolean; 参数1:object nextProps ; 参数2:object nextState
  2. 在接收到新的 props 或者 state,将要渲染之前调用 ; 该方法在初始化渲染的时候不会调用 ; 使用 forceUpdate 方法的时候也不会
  3. 如果确定新的 props 和 state 不会导致组件更新,则此处应该 返回 false。默认情况下,shouldComponentUpdate 总会返回 true
  4. 如果 shouldComponentUpdate返回false,则 render() 将不会执行,直到下一次 state 改变。另外,componentWillUpdate 和 componentDidUpdate 也不会被调用
  5. 例子

    React.createClass({
      shouldComponentUpdate(nextProps, nextState) {
      return false
      }
    });
    

     

setState 阶段:

// component.js
setState(state, callback) {
  let s = this.state;
  if (!this.prevState) this.prevState = extend({}, s);
  extend(s, typeof state==='function' ? state(s, this.props) : state);
  if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
  enqueueRender(this);
}

实现的简单粗暴,在 setState 的时候就进行了合并,会立即改写 this.state,在第一次 setState 时会保留 state 状态到 prevState。由于是立即合并state,如果入参state是函数,props 将只是当前 this.props。

更新: componentWillReceiveProps

  1. 在组件接收到新的 props 的时候调用 ; 在初始化渲染的时候,该方法不会调用
  2. 用此函数可以作为 react 在 prop 传入之后, render() 渲染之前更新 state 
  3. 老的 props 可以通过 this.props 获取到
  4. 在该函数中调用 this.setState() 将不会引起第二次渲染。
  5. 注意:对于 state,没有相似的方法,如果需要在 state 改变的时候执行一些操作,请使用 componentWillUpdate
  6. 例子

    React.createClass({
      componentWillReceiveProps(nextProps) {
       this.setState({
      })
      }
    });
    

     

React

关键代码:

更新: componentDidUpdate

  1. 参数1:object prevProps  ; 参数2:object prevState
  2. 在组件的更新已经同步到 DOM 中之后立刻被调用。该方法不会在初始化渲染的时候调用
  3. 使用该方法可以在组件更新之后操作 DOM 元素。
  4. 注意:为了兼容 v0.9,DOM 节点会作为最后一个参数传入。如果使用这个方法,你仍然可以使用 this.getDOMNode()来访问 DOM 节点
  5. 例子

    React.createClass({
      componentDidUpdate(nextProps, nextState) {
      ...
      }
    });
    

     

 

 

Preact

关键代码:

移除: componentWillUnmount

  1. 在组件从 DOM 中移除的时候立刻被调用
  2. 在该方法中执行任何必要的清理,比如无效的定时器,或者清除在 componentDidMount 中创建的 DOM 元素
  3. 例子

    React.createClass({
      componentWillUnmount() {
        ...
      }
    });
    

     

划重点

相同点:

  • 在 componentWillReceiveProps 中 setState 都会应用到 nextState。
  • 在 shouldComponentUpdate 中 setState 都会应用到 nextState,但是可以直接操作传入的 nextState。

不同点:

  • React下 setState 的值不会立即生效,会一直积累到 componentWillReceiveProps,在此之后会进行合并,并提供给后续生命周期。而Preact下 setState 会立即反映到 this.state,但是,在更新组件的生命周期到 render 前(eg: shouldComponentUpdate), this.state 将会是 prevState。
  • shouldComponentUpdate 阶段 setState 虽然不会影响到最终 state 的值,但是Preact下会影响 this.state 的值,比如之后 componentWillUpdate 中的 this.state, 总之此阶段不要 setState 反正也没用。
  • setState 如果使用函数修改,Preact下传入的 props 将会是 prevProps,而React中是 nextProps,在 componentWillReceiveProps 中 setState 时要注意。

更新: componentWillUpdate

  1. 参数1:object nextProps  参数2:object nextState
  2. 在接收到新的 props 或者 state 之前立刻调用。在初始化渲染的时候该方法不会被调用。
  3. 注意:你不能在刚方法中使用 this.setState()。如果需要更新 state 来响应某个 prop 的改变,请使用 componentWillReceiveProps
  4. 例子

    React.createClass({
      componentWillUpdate(nextProps, nextState) {
      ...
      }
    });
    

     

 

路由配置如下:

<Route path="/" component={App}>
  <IndexRoute component={Home}/>
  <Route path="invoices/:invoiceId" component={Invoice}/>
  <Route path="accounts/:accountId" component={Account}/>
</Route>

3. 当用户从 /invoice/123 跳转到 /invoice/789

  • 所有的组件之前都已经被挂载, 所以只是从 router 更新了 props. 

上海时时乐走势图官网 4

 

生命周期方法


组件的生命周期分成三个状态:

    • Mounting:已插入真实 DOM
    • Updating:正在被重新渲染
    • Unmounting:已移出真实 DOM

React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。

    • componentWillMount()
    • componentDidMount()
    • componentWillUpdate(object nextProps, object nextState)
    • componentDidUpdate(object prevProps, object prevState)
    • componentWillUnmount()

 

此外,React 还提供两种特殊状态的处理函数。

    • componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
    • shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

 

 

例子: 路由切换时,组件生命周期的变化情况


本文由上海时时乐走势图发布于web前端,转载请注明出处:React 生命周期上海时时乐走势图官网

您可能还会对下面的文章感兴趣: