react對useState進行了封裝,調用了mountState。react
function useState<S>( initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<S>>] { currentHookNameInDev = 'useState'; mountHookTypesDev(); const prevDispatcher = ReactCurrentDispatcher.current; ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV; try { return mountState(initialState); } finally { ReactCurrentDispatcher.current = prevDispatcher; } }
若是initialState是函數還能夠執行。
生成一個dispatch方法,經過閉包綁定當前states。
把初始值存到memoizedState上。這個memoizedState綁定到fiber樹上。用來存儲state。閉包
function mountState<S>( initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<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< BasicStateAction<S>, > = (queue.dispatch = (dispatchAction.bind( null, // Flow doesn't know this is non-null, but we do. ((currentlyRenderingFiber: any): Fiber), queue, ): any)); return [hook.memoizedState, dispatch]; }
react其實不知道咱們調用了幾回useState。
因此仍是在memoizedState上動手腳,這個處理體如今mountWorkInProgressHook函數
memoizedState: { baseState, next, baseUpdate, queue, memoizedState }
memoizedState.next
就是下一次useState的hook對象。this
hook1 === Fiber.memoizedState state1 === hook1.memoizedState state2 = hook1.next.memoizedState
由於以這種方式存儲,因此usestate必須在functionalComponent的根做用域中。不能被for,和if。spa
mountState函數返回的是 return [hook.memoizedState, dispatch];
dispatch經過閉包就能夠處理state。code
useState在更新的時候是調用的updateState,這個函數實際上是封裝的updateReducer。orm
function renderWithHooks(){ ReactCurrentDispatcher.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate; }; HooksDispatcherOnMount: { useState: mountState, } HooksDispatcherOnUpdate: { useState: updateState, }
能夠看到updateReducer把新的fiber中的state值更新,返回新的值。而後後續走渲染流程。(以前寫過reat 的渲染流程)
還能夠看到這有個循環update = update.next; while (update !== null && update !== first);
這就是hooks的batchUpdate。對象
function updateReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { 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 < 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 > 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 && 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<A> = (queue.dispatch: any); return [hook.memoizedState, dispatch]; }