Blog icon indicating copy to clipboard operation
Blog copied to clipboard

setState对比useState

Open leibnizi opened this issue 6 years ago • 1 comments

setState

首先声明class组件App,由图可知App内的this方法继承了Component的方法 从react import Component之后可以console.log看出component在react文件夹开发文件react.development.js中

function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

setState的方法写在了Component的prototype上

Component.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

然后又调用了来自react-dom.development.js的enqueueSetState

  enqueueSetState: function (inst, payload, callback) {
    var fiber = get(inst);
    var currentTime = requestCurrentTime();
    var expirationTime = computeExpirationForFiber(currentTime, fiber);

    var update = createUpdate(expirationTime);
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      {
        warnOnInvalidCallback$1(callback, 'setState');
      }
      update.callback = callback;
    }

    flushPassiveEffects();
    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  }

react-dom中的get方法再将App的this转换成fiber

    function get(key) {
      return key._reactInternalFiber;
    }

调度程序调用requestCurrentTime来计算到期时间 ,到期时间决定了批量更新的方式,

追踪两个时间"renderer time" "scheduler time","renderer time"随时更新(就是最小化渲染), "scheduler time"在没有没有pending work时更新。 flushPassiveEffects方法取消被动的方法保证追踪的正常 enqueueUpdate来创建更新队列 scheduleWork是最后的渲染,清空执行队列更新子节点的expiration time

leibnizi avatar Mar 25 '19 12:03 leibnizi

源码中的useState


function resolveDispatcher() {
  var dispatcher = ReactCurrentDispatcher.current;
  !(dispatcher !== null) ? invariant(false, 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.') : void 0;
  return dispatcher;
}
function useState(initialState) {
  var dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

首次加载useState

每次调用useState的第二个参数都会触发rerender 调用下面的代码

useState: function useState(initialState){
     // ...
}

将currentHookNameInDev添加到mountHookTypesDev中 再调用mountState

    function mountState(initialState) {
      var hook = mountWorkInProgressHook();

      if (typeof initialState === 'function') {
        initialState = initialState();
      }

      hook.memoizedState = hook.baseState = initialState;
      var queue = hook.queue = {
        last: null,
        dispatch: null,
        eagerReducer: basicStateReducer,
        eagerState: initialState
      };
      var dispatch = queue.dispatch = dispatchAction.bind(null, // Flow doesn't know this is non-null, but we do.
      currentlyRenderingFiber$1, queue);
      return [hook.memoizedState, dispatch];
    }

hook.memoizedState就是初始化的state,dispatch就是第二个参数相当于之前的setstate

function dispatchAction(fiber, queue, action){
    // ...
}

也会像setState一样记录currentTime和_expirationTime再创建一个_update2

    var currentTime = requestCurrentTime();
    var _expirationTime = computeExpirationForFiber(currentTime, fiber);

    var _update2 = {
      expirationTime: _expirationTime,
      action: action,
      eagerReducer: null,
      eagerState: null,
      next: null
    };

而这个_update2会被加入到queue上

leibnizi avatar Mar 25 '19 13:03 leibnizi