SetState is the second parameter in the array returned by useState. It can be passed as a function. The react official document does not record such a use
setState(value => {
const newValue = alignInRange(value + offset);
return newValue;
});
Copy the code
First look at the useState code
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
if (__DEV__) {
currentHookNameInDev = 'useState';
}
return useReducer(
basicStateReducer,
// useReducer has a special case to support lazy useState initializers
(initialState: any),
);
}
Copy the code
So useState is based on useReducer, and basicStateReducer passes in the first parameter of useReducer. Let’s see the basicStateReducer code
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
}
Copy the code
Let’s review the use of useReducer
const [state, dispatch] = useReducer(reducer, initialState);
Copy the code
Can dispatch take a function as an input? If you look at the last line of useReducer, it returns
const dispatch = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingComponent,
queue,
)));
return [workInProgressHook.memoizedState, dispatch];
Copy the code
That corresponds to the state and dispatch that useReducer returns. Dispatch is a copy of dispatchAction, This points to the useReducer currentlyRenderingComponent with initial parameters and the queue look at dispatchAction pass in a function will happen?
Function dispatchAction<A>(componentIdentity: Object, queue: UpdateQueue<A>, action: A,) { didScheduleRenderPhaseUpdate = true; Const update: update <A> = {action, // action in the update object next: null,}; If (renderPhaseUpdates === null) {// renderPhaseUpdates = new Map(); } const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue); If (firstRenderPhaseUpdate === undefined) {renderPhaseUpdates. Set (queue, update); } else { // Append the update to the end of the list. let lastRenderPhaseUpdate = firstRenderPhaseUpdate; while (lastRenderPhaseUpdate.next ! == null) { lastRenderPhaseUpdate = lastRenderPhaseUpdate.next; / / find the last updated} lastRenderPhaseUpdate. Next = update; // Modify the last update next point to point to this time to be updated}}Copy the code
So what the dispatchAction function does is it adds the action that needs to be updated to the end of the list of actions that need to be updated so when you call dispatch it creates an update, After all the updates have been collected we will schedule a React update which will execute our function Component, which will execute useState again, which will execute useReducer so let’s look at the useReducer code (after removing the non-key code)
export function useReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init? : I => S, ): [S, Dispatch<A>] { currentlyRenderingComponent = resolveCurrentlyRenderingComponent(); workInProgressHook = createWorkInProgressHook(); // Return Hook with memoizedState,queue,next, // This is a re-render. Apply the new render phase updates to the previous // current hook queue: UpdateQueue<A> = (workInProgressHook.queue: any); const dispatch: Dispatch<A> = (queue.dispatch: any); if (renderPhaseUpdates ! == null) { // Render phase updates are stored in a map of queue -> linked list const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue); if (firstRenderPhaseUpdate ! == undefined) { renderPhaseUpdates.delete(queue); let newState = workInProgressHook.memoizedState; let update = firstRenderPhaseUpdate; do { // Process this render phase update. We don't have to check the // priority because it will always be the same as the current // render's. const action = update.action; newState = reducer(newState, action); update = update.next; } while (update ! == null); workInProgressHook.memoizedState = newState; return [newState, dispatch]; } } return [workInProgressHook.memoizedState, dispatch]; }Copy the code
After the execution of useReducer, we get the Hook object, and the queue property in it saves what needs to be updated, and we update it in turn. Lines 10-31 above are the code to execute the update, and line 23 of action is the input parameter for our call to Dispatch. The Reducer useReducer is passed in when useState is called, which is the basicStateReducer mentioned above
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
}
Copy the code
So if we pass a function to our action, it will be executed, and the input is the pre-update state.