宋宣薇 發表於 2019-7-31 17:50:00

react 16 Hooks渲染流程

<h3 id="usestate">useState</h3>
<p>react对useState进行了封装,调用了mountState。</p>
<pre><code class="language-js">function useState&lt;S&gt;(
    initialState: (() =&gt; S) | S,
): {
    currentHookNameInDev = 'useState';
    mountHookTypesDev();
    const prevDispatcher = ReactCurrentDispatcher.current;
    ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
    try {
      return mountState(initialState);
    } finally {
      ReactCurrentDispatcher.current = prevDispatcher;
    }
}
</code></pre>
<h3 id="mountstate">mountState</h3>
<p>如果initialState是函数还可以执行。<br>
生成一个dispatch方法,通过闭包绑定当前states。<br>
把初始值存到memoizedState上。这个memoizedState绑定到fiber树上。用来存储state。</p>
<pre><code class="language-js">function mountState&lt;S&gt;(
initialState: (() =&gt; S) | S,
): {
    // 把hooks加入queue,实际上是为了保证执行顺序。
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
    initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
    last: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
});
const dispatch: Dispatch&lt;
    BasicStateAction&lt;S&gt;,
&gt; = (queue.dispatch = (dispatchAction.bind(
    null,
    // Flow doesn't know this is non-null, but we do.
    ((currentlyRenderingFiber: any): Fiber),
    queue,
): any));
return ;
}
</code></pre>
<h2 id="memoizedstate">memoizedState</h2>
<p>react其实不知道我们调用了几次useState。<br>
所以还是在memoizedState上动手脚,这个处理体现在mountWorkInProgressHook</p>
<pre><code class="language-js"> memoizedState: {
baseState,
next,
baseUpdate,
queue,
memoizedState
}
</code></pre>
<p><code>memoizedState.next</code>就是下一次useState的hook对象。</p>
<pre><code>hook1 === Fiber.memoizedState
state1 === hook1.memoizedState
state2 = hook1.next.memoizedState
</code></pre>
<blockquote>
<p>因为以这种方式存储,所以usestate必须在functionalComponent的根作用域中。不能被for,和if。</p>
</blockquote>
<h1 id="setstate">setstate</h1>
<p>mountState函数返回的是 <code>return ;</code><br>
dispatch通过闭包就可以处理state。</p>
<h2 id="更新">更新</h2>
<p>useState在更新的时候是调用的updateState,这个函数其实是封装的updateReducer。</p>
<pre><code class="language-js">function renderWithHooks(){
    ReactCurrentDispatcher.current =
      nextCurrentHook === null
      ? HooksDispatcherOnMount
      : HooksDispatcherOnUpdate;
};
HooksDispatcherOnMount: {
    useState: mountState,
}
HooksDispatcherOnUpdate: {
    useState: updateState,
}
</code></pre>
<h3 id="updatereducer">updateReducer</h3>
<p>可以看到updateReducer把新的fiber中的state值更新,返回新的值。然后后续走渲染流程。(之前写过reat 的渲染流程)<br>
还可以看到这有个循环<code>update = update.next; while (update !== null &amp;&amp; update !== first);</code><br>
这就是hooks的batchUpdate。</p>
<pre><code class="language-js">function updateReducer&lt;S, I, A&gt;(
reducer: (S, A) =&gt; S,
initialArg: I,
init?: I =&gt; S,
): {
const hook = updateWorkInProgressHook();
const queue = hook.queue;

queue.lastRenderedReducer = reducer;

// ...
// The last update in the entire queue
const last = queue.last;
// The last update that is part of the base state.
const baseUpdate = hook.baseUpdate;
const baseState = hook.baseState;

// Find the first unprocessed update.
let first;
if (baseUpdate !== null) {
    if (last !== null) {
      // For the first update, the queue is a circular linked list where
      // `queue.last.next = queue.first`. Once the first update commits, and
      // the `baseUpdate` is no longer empty, we can unravel the list.
      last.next = null;
    }
    first = baseUpdate.next;
} else {
    first = last !== null ? last.next : null;
}
if (first !== null) {
    let newState = baseState;
    let newBaseState = null;
    let newBaseUpdate = null;
    let prevUpdate = baseUpdate;
    let update = first;
    let didSkip = false;
    do {
      const updateExpirationTime = update.expirationTime;
      if (updateExpirationTime &lt; renderExpirationTime) {
      // Priority is insufficient. Skip this update. If this is the first
      // skipped update, the previous update/state is the new base
      // update/state.
      if (!didSkip) {
          didSkip = true;
          newBaseUpdate = prevUpdate;
          newBaseState = newState;
      }
      // Update the remaining priority in the queue.
      if (updateExpirationTime &gt; remainingExpirationTime) {
          remainingExpirationTime = updateExpirationTime;
      }
      } else {
      markRenderEventTimeAndConfig(
          updateExpirationTime,
          update.suspenseConfig,
      );

      // Process this update.
      if (update.eagerReducer === reducer) {
          // If this update was processed eagerly, and its reducer matches the
          // current reducer, we can use the eagerly computed state.
          newState = ((update.eagerState: any): S);
      } else {
          const action = update.action;
          newState = reducer(newState, action);
      }
      }
      prevUpdate = update;
      update = update.next;
    } while (update !== null &amp;&amp; update !== first);

    if (!didSkip) {
      newBaseUpdate = prevUpdate;
      newBaseState = newState;
    }

    // Mark that the fiber performed work, but only if the new state is
    // different from the current state.
    if (!is(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState;
    hook.baseUpdate = newBaseUpdate;
    hook.baseState = newBaseState;

    queue.lastRenderedState = newState;
}

const dispatch: Dispatch&lt;A&gt; = (queue.dispatch: any);
return ;
}
</code></pre><br><br>
来源:https://www.cnblogs.com/dh-dh/p/11278022.html
頁: [1]
查看完整版本: react 16 Hooks渲染流程