This is the 8th day of my participation in the First Challenge 2022. For details: First Challenge 2022.

React version: V17.0.3

React class components create updates in three different ways: reactdom. render, setState, and forceUpdate. Reactdom. render was introduced in reactDom. render in React source code. In this article, we will introduce the setState and forceUpdate methods for creating updates.

setState

When we initiate an update in a class component, the setState method is usually called. SetState is the primary way to trigger a component update. It queues changes to the component state and tells React that it needs to rerender the component and its children with the updated state.

SetState is defined in React.Component:

/ / the react/SRC/ReactBaseClasses. Js / / add setState method on the prototype, using setState components/update/partialState to update the state, SetState ({}, The callback) Component. The prototype. SetState = function (partialState, callback) {/ / whether partialState accords with a condition, Error if (typeof partialState! == 'object' && typeof partialState ! == 'function' && partialState ! = null ) { throw new Error( 'setState(...) : takes an object of state variables to update or a ' + 'function which returns an object of state variables.', ); } / / state update mechanism, to realize this. In the react - dom updater. EnqueueSetState (this, partialState, callback, 'setState); };Copy the code

As you can see, the first argument to setState must be either an object or a function and must not be null, otherwise an error will be reported. Then call enqueueSetState to initialize setState.

enqueueSetState

The purpose of enqueueSetState is to create an update to the Fiber object when setState is called, and then add the update object to updateQueue and enter the task scheduling process.

/ / react - the reconciler/SRC/ReactFiberClassComponent. New. Js / / inst is called this. SetState when passed this, // The payload is the initial state passed when this. SetState is called. // It can be an Object or a function, // If we want to read this.state immediately after calling setState, Get enqueueSetState(inst, payload, Callback) {// Get the fiber object from the class component instance // this._reactinternals Store the fiber object on this with the _reactInternals attribute const fiber = getInstance(inst); Const eventTime = requestEventTime(); const eventTime = requestEventTime(); // Create a priority variable const lane = requestUpdateLane(fiber); // Create an update object const update = createUpdate(eventTime, lane); Update. Payload = payload; // Add stateState to update. if (callback ! == undefined && callback ! == null) { if (__DEV__) { warnOnInvalidCallback(callback, 'setState'); } // Add setState's second argument, callback, to the update object. } // Add the new update to the update list enqueueUpdate(fiber, update, lane); Const root = scheduleUpdateOnFiber(Fiber, Lane, eventTime); / /... },Copy the code
  • In enqueueSetState, the Fiber object stored on the ClassComponent instance is first obtained using the getInstance method.

    // 通过 class组件实例获取 fiber 对象
    // this._reactInternals 在 this 上通过 _reactInternals 属性存储fiber对象
    const fiber = getInstance(inst);
    Copy the code

The getInstance method is as follows:

// shared/ReactInstanceMap.js

export function get(key) {
  return key._reactInternals;
}
Copy the code

Here is the printed version of the ClassComponent’s this, on which the fiber object is stored via the _reactInternals property.

  • Then calculate the current time and update priority, create a new update object based on the current time and update priority, and add the update state object and callback passed in by setState to the update object:

    Const eventTime = requestEventTime(); const eventTime = requestEventTime(); // Create a priority variable const lane = requestUpdateLane(fiber); // Create an update object const update = createUpdate(eventTime, lane); // Add the state object passed in by stateState to update. Update. if (callback ! == undefined && callback ! == null) { // ... Update. Callback = callback; // Add the second setState argument callback to the update object. }Copy the code
  • Next add the update object to updateQueue, which is a circular linked list:

    // Add the new update to the update list enqueueUpdate(fiber, update, lane);Copy the code
  • Finally, the scheduleUpdateOnFiber method is called to enter the task scheduling process:

    Const root = scheduleUpdateOnFiber(Fiber, Lane, eventTime);Copy the code

forceUpdate

ForceUpdate will force the component to be rerendered. When some variable is not in state, but you want it to be updated; Or a variable in state is so deep that it doesn’t automatically trigger render when updated. At these times we can manually call forceUpdate to force render.

ForceUpdate is defined in react.component.exe:

. / / the react/SRC/ReactBaseClasses js / / in the deep change of Component but not call setState, using this method, the forced Component update once, No matter whether props/state update Component. The prototype. ForceUpdate = function (the callback) {this. Updater. EnqueueForceUpdate (this, callback, 'forceUpdate'); };Copy the code

As you can see, forceUpdate calls enqueueForceUpdate to trigger render.

enqueueForceUpdate

EnqueueForceUpdate creates an update to the Fiber object when forceUpdate is called, and then adds the update object to the updateQueue and enters the task scheduling process.

// react-reconciler/src/ReactFiberClassComponent.new.js enqueueForceUpdate(inst, Callback) {// Get the fiber object from the class component instance // this._reactinternals Store the fiber object on this with the _reactInternals attribute const fiber = getInstance(inst); Const eventTime = requestEventTime(); const eventTime = requestEventTime(); // Create a priority variable const lane = requestUpdateLane(fiber); // Create an update object const update = createUpdate(eventTime, lane); Update. Tag = ForceUpdate; // Update. if (callback ! == undefined && callback ! == null) { if (__DEV__) { warnOnInvalidCallback(callback, 'forceUpdate'); } // Add setState's second argument, callback, to the update object. } // Add the new update to the update list enqueueUpdate(fiber, update, lane); Const root = scheduleUpdateOnFiber(Fiber, Lane, eventTime); / /... },Copy the code

The flow of the enqueueForceUpdate method is similar to that of enqueueSetState, except that the value of the attribute tag is changed:

Update. Tag = ForceUpdate; // Update.Copy the code

When you create an update object, the default value of the tag attribute is 0, that is, update. When you execute forceUpdate, you need to change the value of the tag attribute to 2, that is, forceUpdate.

As you can see in the createUpdate method, the initial value of the tag attribute is UpdateState:

//react-reconciler/src/ReactUpdateQueue.new.js export function createUpdate(eventTime: number, lane: Lane): Update<*> { const update: Update<*> = { eventTime, lane, // export const UpdateState = 0; // export const ReplaceState = 1; // export const ForceUpdate = 2; // export const CaptureUpdate = 3; // Update tag: UpdateState, // Update tag payload: null, callback: null, next: null,}; return update; }Copy the code

Because forceUpdate triggers updates, you need to change the tag to forceUpdate so React can prioritize updates.

conclusion

SetState is the main way to perform component updates, forceUpdate is to force components to perform updates, and both update processes are similar:

  1. Gets the Fiber object on the ClassComponent’s this object

  2. Calculates the current time and update priority

  3. Create an Update object based on the current time and update priority

  4. Add the state object to be updated in setState and the callback to be performed to the Update object

  5. Adds the current Update object to the Update Ue queue

  6. The task scheduling process is displayed

The only difference is that the update object’s tag value changes to forceUpdate when forceUpdate is executed. The default value of the tag attribute of the update object is UpdateState. If forceUpdate is executed, the value is changed to forceUpdate to facilitate the React update priority sorting.