There are three articles in this series:

Quick question before class

  1. Why is it sometimes that multiple setstates in a row only take effect once?
  2. Can I get the value of state after setState?
  3. Is setState synchronous or asynchronous?
  4. Why are some properties (such as ref) not available from props?
  5. Why does a controlled form component fail to enter content if it sets value?
  6. Why can’t react event objects be retained?


The directory structure

Top-level directory

React adopts monorepo’s management style. The repository contains multiple separate packages so that changes can be tuned together and problems can only occur in the same place.

packages

  • I don’t know how to react
  • The React-Reconciler is the core code of the newly implemented Fiber Reconciler in 16.x
  • Scheduler is the core code of the React Scheduling module, which was previously placed in the React-Reconciler but later became independent
  • Events is the code associated with react events
  • Shared is code that is common to different packages
  • We will not discuss other packages here

The core module

React “Core” contains all global React apis, such as:

  • React.createElement()
  • React.Component
  • React.Children

The React core contains only the apis necessary to define components. It contains no coordination algorithms or other platform-specific code. It works with both React DOM and React Native components. React core code is in the source code packages/ React directory. Released as the React package on NPM. The corresponding standalone browser build, called React.js, exports a global object called React.

The renderer

React was originally intended for the DOM, but has since been adapted to support React Native as well. Therefore, the concept of a “renderer” was introduced into the React internal mechanism. The renderer is used to manage a React tree to make different calls depending on the underlying platform. The renderer is also in the packages/ directory:

reconciler

Even though React DOM and React Native renderers are very different, they still need to share some logic. In particular, the coordination algorithms need to be as similar as possible so that features such as declarative rendering, custom components, state, lifecycle methods, and REFs work consistently across platforms. To solve this problem, different renderers share some code with each other. We call this part of the React the Reconciler. When dealing with updates like setState(), the Reconciler calls render() on the components in the tree, and then decides whether to mount, update, or uninstall. The Reconciler does not have a separate package because they currently have no public API. Instead, they are excluded from renderers such as React DOM and React Native. This part of the source code is in/Packages/React-Reconciler.

scheduler

In the react document, I said that the process between generating updates and manipulating the DOM is called reconciliation. The process can also be divided into a reconciliation queue, where the updates are placed in an update queue. The section on how to schedule these updates for the next task is called scheduler, while React uses a method called Cooperative Scheduling to schedule tasks, which simply means taking full advantage of the browser’s idle time to execute tasks. If you have free time, you can perform the task, and if you don’t, you can give it to the browser. In the browser, this is done through the requestIdleCallback API. However, due to some problems with the API and compatibility of the browser, React simulates requestIdleCallback’s behavior via requestAnimationFrame, setTimeout, and MessageChannel. Now React has this part of the code singled out as a package. This source code is in /packages/ Scheduler.

The event system

React implements its own event system, which reduces memory consumption compared to the native DOM event system and flattens browser differences. How react does this?

  • Event delegates are used to register events on the Document object
  • React internally creates its own event object, SyntheticEvent, that encapsulates native events, and it’s actually this encapsulated object that we manipulate in React
  • React internally creates and destroys event objects in the form of object pools

The source code for this part is in /packages/events.

Built-in objects

FiberRoot

FiberRoot is the entry object for the entire application. It is a javascript object that internally records a lot of global information related to application updates, such as the container to be mounted.

function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  // The corresponding Fiber object for the current application is Root Fiber
  this.current = null;
  // The root node, the second parameter received by the render method
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.pingCache = null;
  // Expiration time of finishedWork
  this.finishedExpirationTime = NoWork;
  // Complete the RootFiber object in reconciliation, and then enter the Commit phase
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.firstBatch = null;
  this.callbackNode = null;
  this.callbackExpirationTime = NoWork;
  this.firstPendingTime = NoWork;
  this.lastPendingTime = NoWork;
  this.pingTime = NoWork;

  if (enableSchedulerTracing) {
    this.interactionThreadID = unstable_getThreadID();
    this.memoizedInteractions = new Set(a);this.pendingInteractionMap = new Map();
  }
}
Copy the code

Fiber

function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode,) {
  // Instance
  // Mark the different component types
  this.tag = tag;
  // The key in ReactElement
  this.key = key;
  // ReactElement. Type, which is the first argument in our call to 'createElement'
  this.elementType = null;
  // What is returned after the resolved asynchronous component, typically 'function' or 'class'
  this.type = null;
  // Element (DOM, class, etc.) corresponding to the current Fiber object
  this.stateNode = null;

  // Fiber
  // Point to the parent fiber
  this.return = null;
  // Point to the first child
  this.child = null;
  // Point to the sibling node
  this.sibling = null;
  // The index of the nodes in the array is compared by the diff algorithm
  this.index = 0;

  this.ref = null;

  // New changes bring new props
  this.pendingProps = pendingProps;
  // Props since the last render
  this.memoizedProps = null;
  // Updates generated by components corresponding to this Fiber are stored in this queue
  this.updateQueue = null;
  // The state of the last render
  this.memoizedState = null;
  this.contextDependencies = null;

  this.mode = mode;

  // Effects
  // Use to record the Effect of oneself
  this.effectTag = NoEffect;
  // The single linked list is used to quickly find the next side effect
  this.nextEffect = null;

  // The first side effect in the subtree
  this.firstEffect = null;
  // The last side effect in the subtree
  this.lastEffect = null;

  // Represents a point in the future when the task should be completed
  this.expirationTime = NoWork;
  // The earliest expiration time in the subtree
  this.childExpirationTime = NoWork;

  // And its corresponding Fiber object
  // current <=> workInProgress
  this.alternate = null;
}
Copy the code

ExpirationTime

In the last article, we said React first just had a few Priority variables assigned to it, but this would cause hunger. Low-priority tasks would get interrupted all the time. Then React introduced expirationTime. Even low priority tasks as long as the expiration time to also can force executed immediately, so expirationTime is how to calculate out, you can refer to the following process:

Online address: www.processon.com/view/link/5…

If you don’t understand my expirationTime context, you would like to see a expirationTime context. You would like to see a expirationTime context.

Render = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} One is to make updates that are triggered within 25/10 ms have the same expiration time so that they can be updated in batches to improve performance. The second is to increase the priority of a high-priority task by making it expire longer than a low-priority task.

Core API

createElement

React.createElement(
  type,
  [props],
  [...children]
)
Copy the code

CreateElement is a method that creates an element in react. It creates an element of the specified type. The type parameter can be an element DOM tag string, a React Component type (class or function), or a Fragment.

The source code

CreateElement method source code in/react/SRC/ReactElement. Js

export function createElement(type, config, children) {
	// Initialize variables
  const props = {};
  // ...

  // Step 1: Initialize properties
  // Props for props
  RESERVED_PROPS attribute name (key,ref, etc.)
  if(config ! =null) {
    // ...
  }

  // Step 2: Mount children to props. Children
  // If there are multiple children, convert them to an array
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    // ...
    props.children = childArray;
  }

  // Step 3: Parse defaultProps
  if (type && type.defaultProps) {
    // ...
  }
  
  // Step 4: pass the processed variable to the ReactElement constructor
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}
Copy the code
const ReactElement = function (type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    // This tag allows us to uniquely identify it as a React element
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };

  return element;
};
Copy the code

This section addresses the problem

  1. Why some properties (such as ref) cannot be obtained from props

ReactDOM.render

ReactDom. Render source code in/react – dom/SRC/client/ReactDom js

The whole process

Online address: www.processon.com/view/link/5…

Pre-preparation stage

What you do in the preparatory phase can be summed up in three points:

  1. Create fiberRoot and rootFiber objects
  2. Calculate expirationTime
  3. Create an update and put the update in the queue

Online address: www.processon.com/view/link/5…

schedule

See below

render

See below

commit

See below

setState

The whole process

Online address: www.processon.com/view/link/5…

Pre-preparation stage

What you do in the preparatory phase can be summed up in three points:

  1. Calculate expirationTime
  2. Create an update and put the update in the queue

schedule

Note that if the setState method is called from an event function bound on the React Element, workPhase = BatchedEventPhase is set before the setState method is executed. So in the scheduleUpdateOnFiber method the following branch is entered.


flushSyncCallbackQueue
renderRoot



flushSyncCallbackQueue
renderRoot



See below

render

See below

commit

See below

This section addresses the problem

  1. Why is it sometimes that multiple setstates in a row only take effect once?
  2. Can I get the value of state after setState?
  3. Is setState synchronous or asynchronous?

Schedule

An overview of the

Find the fiberRoot node corresponding to the trigger update node, and then adjust the update of this node, which can be divided into two cases: synchronous and asynchronous. Synchronization can be divided into two kinds: Is it LegacyUnbatchedPhase? If it is, it does not need to be scheduled to directly enter the next phase (Render phase), if it is not, it will be put into the next frame to execute immediately. For asynchronous tasks, an expiration time should be calculated according to the priority. Then compare it with the queued tasks to find out which task is about to expire and enter the render phase in the next frame.

The whole process

Online address: www.processon.com/view/link/5…

Core method

scheduleWork

  • Determine nested updates, more than 50 nested updates will report an error
  • Find the fiberRoot object and set expirationTime
  • Determine if a high-priority task is interrupting the current task
  • There are two large phases depending on whether expirationTime is equal to Sync or not. Let’s just call them synchronous and asynchronous
    • The synchronization phase can be divided into two situations
      • Call renderRoot when workPhase equals LegacyUnbatchedPhase
      • Other workphases call scheduleCallbackForRoot, and call flushSyncCallbackQueue when workPhase is NotWorking
    • The asynchronous phase gets the priorityLevel from getCurrentPriorityLevel and then calls scheduleCallbackForRoot

Flow chart: [email protected] Principle interpretation — Scheduling (1)

scheduleCallbackForRoot

  • Determine whether the current root.callbackNode has a lower priority than the newly passed task, and if so, cancel the task
  • Call scheduleSyncCallback if the expirationTime of a new task is Sync
  • If the new task’s expirationTime is not Sync, calculate how much time is left for the task to expire (timeout) and then call scheduleCallback

Flowchart: [email protected] source code parsing — scheduling (2)

scheduleSyncCallback

Then call Scheduler_scheduleCallback to set the priority to Scheduler_ImmediatePriority, and then set the priority to Scheduler_ImmediatePriority. The callback for flushSyncCallbackQueueImpl flowchart: [email protected] source code parsing – scheduling (2)

scheduleCallback

The Scheduler_scheduleCallback Scheduler_scheduleCallback Scheduler_scheduleCallback Scheduler_scheduleCallback Scheduler_scheduleCallback

Scheduler_scheduleCallback (unstable_scheduleCallback)

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Then look at the first waiting for scheduled tasks (firstCallbackNode) is empty, if is empty the newNode as firstCallbackNode then call scheduleHostCallbackIfNeeded, Otherwise is newNode expiration time is currently the earliest in the list, if it is also set it to firstCallbackNode then execute scheduleHostCallbackIfNeeded flowchart: [email protected] source reading – scheduling (3)

scheduleHostCallbackIfNeeded

RequestHostCallback (flushWork, expirationTime) if firstCallbackNode is not null; Flow chart: [email protected] source code interpretation — scheduling (3)

requestHostCallback

Set scheduledHostCallback to the incoming callback. If a callback is currently executing or the expiration time is less than 0, call port.postMessage immediately to indicate immediate execution ScheduledHostCallback and whether the incoming timeout (didTimeout) or call requestAnimationFrameWithTimeout (animationTick); Flow chart: [email protected] source code interpretation — scheduling (four)

requestAnimationFrameWithTimeout

Simulate the requestAnimationFrame to execute the incoming callback on the next frame, because the requestAnimationFrame does not execute when the TAB is running in the background, so set a timer with setTimeout to solve this problem. Cancel the timer if requestAnimationFrame is in effect and vice versa. Flow chart: [email protected] source code interpretation — scheduling (four)

animationTick

If scheduledHostCallback requestAnimationFrameWithTimeout not null then call to arrange the task of the next frame Otherwise it is not waiting for the task of the exit, to calculate the next frame running time (nextFrameTime), Set it to 8ms if less than 8ms, then calculate the frameDeadline for the current frame and then call port.postMessage if there is a task. Flow chart: [email protected] source code interpretation — scheduling (four)

port.onmessage

Port.postmessage is then received by port.onMessage, which determines whether the current frame has time left, and if it does not check whether the task to be executed (scheduledHostCallback) has timed out. Timeout is set didTimeout = true then call requestAnimationFrameWithTimeout without timeout, then quit; ScheduledHostCallback (didTimeout) is executed if there is still time left. Flow chart: [email protected] source code interpretation — scheduling (four)

flushWork

The argument it takes is port.onMessage passed in didTimeout, If didTimeout is true, you would like your first task expirationTime to be lower than the current time. If firstCallbackNode expirationTime is lower than the current time. Less than if you constantly perform flushFirstCallback until firstCallbackNode is empty or firstCallbackNode. ExpirationTime greater than or equal to the current time. If didTimeout is false then continue to flushFirstCallback until firstCallbackNode is empty or the current frame has no time left, Finally, whether the above circumstances will perform scheduleHostCallbackIfNeeded judgment about whether there is any need to perform tasks. Flow chart: [email protected] source code interpretation — scheduling (six)

flushFirstCallback

ImmediatePriority ImmediatePriority ImmediatePriority task ImmediatePriority task ImmediatePriority task ImmediatePriority task ImmediatePriority task ImmediatePriority task FirstCallbackNode may return a callback, insert new callback function to the list, according to the sort of it at the end of time, if the list of new callback is the highest priority is invoked scheduleHostCallbackIfNeeded arrange next execution. Flowchart: [email protected] source code parsing — scheduling (five)

flushSyncCallbackQueueImpl

Eventually executed for scheduleSyncCallback * * * * scheduledHostCallback is flushSyncCallbackQueueImpl this method is the loop syncQueue array of tasks. Flowchart: [email protected] source code parsing — scheduling (five)

flushSyncCallbackQueue

Remember that the scheduleCallbackForRoot will be called initially when the scheduleCallbackForRoot is executed if the workPhase is NotWorking in the synchronization phase, This method first calls Scheduler_cancelCallback cancel immediateQueueCallbackNode, then executes flushSyncCallbackQueueImpl is that method above, The callback immediateQueueCallbackNode corresponding is flushSyncCallbackQueueImpl, So I think that this method is called immediately to implement the callback in syncQueue flushSyncCallbackQueueImpl tasks instead of waiting for the next frame is carried out.

Render (reconciliation)

An overview of the

Start with rootFiber and loop through each node of the fiber tree. For each node, different update methods are called according to the node type. For example, for class components, instance objects are created, new state is calculated by calling updateQueue, and life cycle functions are executed. For example, HostComponent will create a fiber object for its children. When one side of the subtree is completed, it will complete the operation, which is to create the corresponding DOM node and add it to the parent node and set the effect chain of the parent node. Then iterate over the sibling nodes and do the same to the sibling nodes, so that the tree is updated and the next phase (Commit Phase) is completed.

The whole process

Online address: www.processon.com/view/link/5…

Core method

renderRoot

RenderRoot is the core method of the entire Render phase and is the entry method for the entire phase. After entering the method, First, you will determine if your new fiberRoot is inconsistent with the previous fiberRoot you are processing (workInProgressRoot) or if your current expirationTime is not equal to the rendering task you are executing ExpirationTime (renderExpirationTime), then prepare Stack, If isSync is true (expirationTime = = = Sync or priorityLevel = = = ImmediatePriority) and is an asynchronous task and also is out of date, so the immediate execution renderRoot, The expirationTime is the current date. If isSync is not true, the current task does not time out, then set currentEventTime = NoWork. This will give you a new time the next time you request currentTime. If isSync is true then call workLoopSync and if isSync is true then call workLoop. We’ll talk about these two methods separately, but I’m not going to list them here, and then we’re going to do exception handling if there’s an error in one of these methods. CommitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot: commitRoot Flow chart: [email protected] source code analysis — Render Phase (renderRoot)

prepareFreshStack

This method is some preparation before the task starts, I was wondering where the workInProgress was initialized, which is actually right here, CreateWorkInProgress is called to copy a workInProgress fiber object based on rootFiber, and then some other global variables are set.

workLoopSync

WorkLoopSync is a simple internal loop call to performUnitOfWork. The criterion is whether the return value workInProgress of performUnitOfWork is null.

workLoop

WorkLoop is similar to workLoopSync, except that the loop’s termination condition shouldYield is added. ShouleYield determines if the current task should be interrupted if it has not timed out and has run out of time. At this point, the workLoop will terminate.

prerformUnitOfWork

PerformUnitOfWork is a method called by both workLoopSync and workLoop methods. Within it, the beginWork method is called, and the beginWork method returns the next task to be performed (next). If next is empty, it means that the leaf node has been traversed, then the call to completeUnitOfWork can execute the completion logic. See the previous article for details.

beginWork

The beiginWork method receives the completed fiber node (current), the executing fiber node (workInProgress), and the current expirationTime. If this is the first rendering, set didReceiveUpdate to false. If this is not the first rendering, determine whether props or context changed, and if so, set didReceiveUpdate to true, }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} And after doing some processing on different types of elements based on the type of workInProgress, it exits because the current task has no updates that need to be performed. Then for the first render or for an update, we call the update method on the element type again based on the type of workInProgress. For ClassComponent, for example, we call updateClassComponent. This method returns the next node to perform performUnitOfWork, which is its child node. Flow chart: [email protected] source code analysis — Render phase (beginWork)

completeUnitOfWork

When beginWork returns the node (next) is empty, it will call completeUnitOfWork, that has been traversed to the leaf of the component, and then will go up to find the parent node to complete the operation, and then traversal the brother node, the entire traversal process can refer to the content of the previous article. This method first sets the workInProgress as the node passed in, then goes into a loop that first checks if the workInProgress is Incomplete (with an exception), and if there is no exception completeWork is executed. If the result returned by the completeWork (next) is not null, then return next and let performUnitOfWork handle it. If next is null, then mount the effect to the parent node. Mount effect from the current node and its children to the parent node. If there is an exception, unwindWork is executed. UnwindWork also returns a node (next). If next is not empty, return performUnitOfWork. Then clear the parent effect chain and mark only an Incomplete effect. If so, return to performUnitOfWork to execute. Otherwise, set workInProgress as the parent node (returnFiber) to continue the loop. Know workInProgress is empty, as the show traverse to the root node, tag workInProgressRootExitStatus = RootCompleted explain the root node has been completed, and then can enter the commit phase, Null is returned. Flowchart: [email protected] source analysis – rendering phase (completeUnitOfWork)

completeWork

Inside this method is a switch that performs different methods on different types of nodes based on the workinProgress. tag. The most common one is the HostComponent operation. If HostComponent is mounted for the first time, create the DOM node using the createInstance method and then mount the dom node to the parent node using the appendAllChildren method. The finalizeInitialChildren method is then called to bind the event to the DOM node, setting the property to the corresponding DOM node (such as style), And then determine if autoFocus is set if it’s a form element and if it’s set then update workInProgress; If so, create a text node with createTextInstance and assign it to workinProgress. stateNode if it is an update process, call updateHostText WorkInProgress marks an update. Flowchart: [email protected] source analysis – rendering stage (completeWork) **

Commit

An overview of the

The main thing to do in the submit phase is to process the effect generated in the Render phase, which is divided into three stages

  • Phase 1: The main life cycle method here is called getSnapshotBeforeUpdate before the DOM operation occurs
  • Phase 2: processing node adding, deleting and changing, the deletion operation needs to do special processing to synchronously delete its child nodes and call the corresponding life cycle function
  • Phase 3: After the DOM operation is complete, the corresponding lifecycle function needs to be called and the callback in updateQueue is executed

The whole process

Online address: www.processon.com/view/link/5…

Core method

commitRoot

CommitRoot accepts the fiberRoot object, then calls the commitRootImpl method and passes the fiberRoot object to it, FlushPassiveEffects: flushPassiveEffects: flushPassiveEffects: flushPassiveEffects: flushPassiveEffects Flow chart: [email protected] Source code analysis — commitRoot

commitRootImpl

First, the finishedWork is obtained according to the incoming fiberRoot, that is, the rootFiber completed in the previous stage. Then, some variables are reset and the effect chain is processed. If the rootFiber also has effect, it needs to be added to the effect chain. These effects are then processed separately through three loops:

  • The first cycle of every effect call commitBeforeMutationEffects methods
  • The second loop calls the commitMutationEffects method for each effect
  • The third loop calls the commitLayoutEffects method for each effect

Finally, call flushSyncCallbackQueue to flushSyncQueue and execute their flowchart if syncQueue has other tasks: [email protected] source code analysis — commitRoot

commitBeforeMutationEffects

This method determines whether effect has a Snapshot. Snapshot is added to the render phase to determine whether the class component has the getSnapshotBeforeUpdate life cycle. If there is a call commitBeforeMutationEffectOnFiber, in this method can judge the type of fiber, CommitHookEffectList is called if it is a function component (I don’t understand why function component has Snapshot), If it is a class components will be executed getSnapshotBeforeUpdate this method and returns the result set to the instance of __reactInternalSnapshotBeforeUpdate attributes, This will be used for componentDidUpdate. Flow chart: [email protected] source – the commit phase is analysed (commitBeforeMutationEffects)

commitMutationEffects

This method performs several effects related to dom operations:

  • ContentReset: resets the text in the node
  • Ref: Previously set Ref to be disassociated
  • Placement: Find its sibling and its parent, and if the parent is a DOM node then insert the target node before the sibling by insertBefore, If the parent node is not a DOM node (HostRoot/HostPortal), find their corresponding container and execute inserBefore.
  • PlacementAndUpdate: Indicates that there is both Placement and Update. The Placement operation is called first, and then the commitWork is called. The commitWork sets the properties of the DOM node.
  • Update: Calls commitWork.
  • Deletion: The deletion operation is a little more complicated, because there may be other nodes below the deleted node, so you need to traverse the subtree to perform the deletion operation. Internally, it is a recursive process. For dom nodes, you call removeChild, The class component is first dereferent (safelyDetachRef) and then called componentWillUnmount.

Note:

  • Refer to this flowchart for Placement inserts where the parent node is not a DOM node
  • The delete operation is traversed in a manner similar to the depth-first traversal of a tree

Flow chart: [email protected] Source code analysis — commitMutationEffects

commitLayoutEffects

This method is to perform the entire commit stage last cycle, the method of internal call two main methods commitLayoutEffectOnFiber and commitAttachRef, inside the first method is a switch for different nodes on different operation:

  • ClassComponent: CommitUpdateQueue (componentDidMount) or componentDidUpdate (componentDidMount). CommitUpdateQueue (processUpdateQueue) is used to process the update. Update (componentDidCatch) callback (componentDidCatch);
  • HostRoot: commitUpdateQueue is also called, because the third argument to the reactdom. render method can also accept a callback
  • HostComponent: Determine if there is an autoFocus and call the focus method to get the focus
  • Other types are not listed for the time being

CommitAttachRef (commitLayoutEffects) is the process of attaching an instance of a node to a re.current.

Synthetic events

injection

The injection phase is performed as soon as the reactdom.js file is loaded, and the main purpose is to create three global variables for later use

The source address

Github.com/LFESC/react…

The flow chart

www.processon.com/view/link/5…

Core method

InjectEventPluginOrder The injectEventPluginOrder method takes an array of strings that define the order in which the plugins are to be injected, then calls the recomputePluginOrdering method, This method adds plugins to the plugins array in the order the plugins were passed in, and calls the publishEventForPlugin method for each event in the plugin. The following code uses the change plugin as an example to give you an intuition of what the method does.

const publishedEvents = pluginModule.eventTypes;
    // const eventTypes = {
    // change: {
    // phasedRegistrationNames: {
    // bubbled: 'onChange',
    // captured: 'onChangeCapture',
    / /},
    // dependencies: [
    // TOP_BLUR,
    // TOP_CHANGE,
    // TOP_CLICK,
    // TOP_FOCUS,
    // TOP_INPUT,
    // TOP_KEY_DOWN,
    // TOP_KEY_UP,
    // TOP_SELECTION_CHANGE,
    / /,
    / /},
    // };
    for (const eventName in publishedEvents) {
      invariant(
        publishEventForPlugin(
          publishedEvents[eventName], // ChangeEventPlugin.eventTypes.change
          pluginModule, // ChangeEventPlugin
          eventName, // change
        ),
        'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', eventName, pluginName, ); }}Copy the code

The publishEventForPlugin method first sets the global variable eventNameDispatchConfigs, and then iterates over phasedRegistrationNames that store the time names that you’ll bind to react elements, Format is as follows:

phasedRegistrationNames: {
  bubbled: 'onChange'.captured: 'onChangeCapture',}Copy the code

Bubbled means triggered in the bubble phase, captured means triggered in the capture phase, and then the publishRegistrationName method is called for each

PublishRegistrationName will be introduced to the method of parameter is set to registrationNameModules and registrationNameDependencies both global variables

ChangeEventPlugin = ChangeEventPlugin = ChangeEventPlugin = ChangeEventPlugin = ChangeEventPlugin

const eventTypes = {
  change: {
    phasedRegistrationNames: {
      bubbled: 'onChange'.captured: 'onChangeCapture',},dependencies: [
      TOP_BLUR,
      TOP_CHANGE,
      TOP_CLICK,
      TOP_FOCUS,
      TOP_INPUT,
      TOP_KEY_DOWN,
      TOP_KEY_UP,
      TOP_SELECTION_CHANGE,
    ],
  },
};
Copy the code

EventNameDispatchConfigs:

{
  change: ChangeEventPlugin.eventTypes.change,
  / /... other plugins
}
Copy the code

RegistrationNameModules:

{
  onChange: ChangeEventPlugin,
  onChangeCapture: ChangeEventPlugin
}
Copy the code

RegistrationNameDependencies:

{
  onChange: ChangeEventPlugin.eventTypes.change.dependencies,
  onChangeCapture: ChangeEventPlugin.eventTypes.change.dependencies
}
Copy the code

Listening to the

The listener phase is in the render phase and the completeWork method is going to call the updateHostComponent method on the HostComponent, which is going to set the props for the DOM node, This includes event-related properties. Here we bind the event to the document instead of itself, which is beneficial to reduce memory overhead and improve performance, and bind different event handlers for interactive and non-interactive types of events.

The flow chart

Online address: www.processon.com/view/link/5…

Core method

The entry method of the finalizeInitialChildren mount event, called in the Render phase completeWork, calls setInitialProperties in this method.

The setInitialProperties method will default to some DOM nodes even if you don’t set the event, such as the load event for the iframe, and then the setInitialDOMProperties method will be executed.

The setInitialDOMProperties method handles props set on the DOM node. These props include style, dangerouslySetInnerHTML, children, and of course event-related properties. Remember the global variable registrationNameModules we set in the injection in the previous section, which comes in handy here to determine if any event-related properties are ready to be bound. If so, we call the ensureListeningTo method.

EnsureListeningTo accepts the second argument that reactdom.render accepts, container and the event name (such as onClick). Determine if the Container is a Document or DocumentFragment node, and if so, pass it to the listenTo method. Otherwise, get the corresponding document from the Element. OwnerDocument and pass it to the listenTo method.

ListenTo first creates a listeningSet for the incoming element. This object is used to store what events the element is listening for. Then through our global object generated during the injection phase registrationNameDependencies access to binding rely on other events (dependencies), to traverse the dependencies, Call trapCapturedEvent for events that you want to listen on during the capture phase, call the trapBubbledEvent method for other events, and finally put the events in the listeningSet.

TrapCapturedEvent/trapBubbledEvent these two methods will be called trapEventForPluginEventSystem, difference between trapCapturedEvent method the third parameter is true, TrapBubbledEvent the third argument will pass false.

TrapEventForPluginEventSystem first determines the incoming event is a Interactive events, namely whether events related to a user interaction, If it’s dispatchInteractiveEvent otherwise it’s dispatchEvent, The addEventCaptureListener or addEventBubbleListener is then called based on the third parameter, capture.

Note: The interaction events defined in React can be seen here

AddEventCaptureListener/addEventBubbleListener these two methods will be called element. The addEventListener differ in that the third parameter is a true one is false, Indicates whether to fire in the capture phase or in the bubble phase.

The trigger

The flow chart

Online address: www.processon.com/view/link/5…

Core method

DispatchInteractiveEvent this method will call discreteUpdates, This method calls runWithPriority and calls the dispatchEvent method with the priority of UserBlockingPriority.

The dispatchEvent method first obtains the event Target object (nativeEventTarget) through the getEventTarget method. Note that the event target corresponds to the element triggered by the event rather than the element bound to the event. Then get the fiber object (targetInst) that corresponds to the target object, look up if you can’t find it, and set targetInst to null if it’s not yet mounted to the DOM. The last call dispatchEventForPluginEventSystem method.

DispatchEventForPluginEventSystem the method and the events of the previous step into the first related parameters (topLevelType nativeEvent, targetInst) stored on an object (bookKeeping), Because it’s going to be created multiple times so react is going to create it in a pool of objects, and then it’s going to call batchedEventUpdates and pass in the handleTopLevel and bookKeeping, Return the Bookkeeping object to the object pool after execution.

The batchedEventUpdates method first determines if the isBatching variable is true. If it is true, it executes the first method it accepts, which is the handleTopLevel passed in the previous step. Otherwise, it sets isBatching to true. Then we execute the batchedEventUpdatesImpl method passing in handleTopLevel and BookKeeping, and when we’re done we execute the batchedUpdatesFinally method.

BatchedEventUpdatesImpl When we see a method that has an Impl behind it and it’s probably implemented through dependency injection, that’s what it does, it defines the implementation of that method based on the platform, The method that we actually call in the DOM environment is batchedEventUpdates, which determines whether the current workPhase is not NotWorking, if it’s not NotWorking we’re probably already in the batch phase, At this point we just execute the incoming method and exit, and if we’re currently in the NotWorking state we’ll set workPhase to BatchedEventPhase and execute the incoming method, Restore workPhase and run flushSyncCallbackQueue.

FlushSyncCallbackQueue: flushSyncCallbackQueue: flushSyncCallbackQueue

HandleTopLevel first creates an array of ancestors through a loop, which typically has only one object, the fiber object corresponding to the DOM node, and then iterates through the array. Acquisition eventTarget, event name (topLevelType) and native event object (nativeEvent), the incoming runExtractedPluginEventsInBatch method.

RunExtractedPluginEventsInBatch extractPluginEvents this method will first call to create an event object, then call runEventsInBatch method to perform it.

The extractPluginEvents method traverses the plugins (that is, the ones created during the injection phase) and then calls the extractEvents method of the plugin (e.g. ChangeEventPlugin), Finally, the events created are returned.

Note: The generation of the Event object is left to the next section.

RunEventsInBatch this method will be called forEachAccumulated incoming events to address is on the step of the incoming events and executeDispatchesAndReleaseTopLevel method, ForEachAccumulated method is a tool, it is just a call executeDispatchesAndReleaseTopLevel method for each of the events.

ExecuteDispatchesAndReleaseTopLevel didn’t do anything just call the method executeDispatchesAndRelease method and pass the event object to it.

ExecuteDispatchesAndRelease this method will be called executeDispatchesInOrder and then determine whether the event needs to persist, if you don’t need to release it.

Note: This explains problem # 6, why the event object cannot be retained because it is destroyed after the event handler executes, unless you manually call the event.persist() method. The source address

Using a dispatchListeners and A dispatchin参 考 Instance on the event object we need to obtain a dispatchListeners and a dispatchin参 考 instance on the event object We then traverse the dispatchListeners to determine if the event has been propagationstopped 参 考 答 案 (dispatchListeners[I]) and instance (dispatchinpO [I]) if there is no block we call the executeDispatch method and pass in the listener (dispatchListeners[I]) and instance (dispatchinpO [I]), After the execution, clear the dispatchListeners and DISPATchinpO files.

The executeDispatch method first gets the event type (event.type) and sets event.currenttarget to the DOM node corresponding to the incoming instance. Then call invokeGuardedCallbackAndCatchFirstError method was introduced into the type, the listener and event, this method will do some internal error capture, Essentially, the listener is called directly and the event is passed in.

BatchedUpdatesFinally, batchedEventUpdates, batchedEventUpdatesImpl, batchedUpdatesFinally, In this method, we first determine whether the restoreQueue or restoreTarget is empty. If not, there is a controlled component that needs to be processed. Call flushDiscreteUpdatesImpl and then you can call flushdiscreteUpdatesupdates immediately on the DOM. RestoreStateIfNeeded is then called, which sets the controlled component’s value to props. Value.

This section addresses the problem

  1. Why does a controlled form component fail to enter content if it sets value?
  2. Why can’t react event objects be retained?

The event object

The flow chart

Online address: www.processon.com/view/link/5…

The source address

Github.com/LFESC/react…

Core method

ExtractEvents Every Event plugin has an extractEvents method that generates event objects. Let’s take ChangeEventPlugin as an example. GetTargetInstFunc, handleEventFunc, getTargetInstFunc, getTargetInstFunc, handleEventFunc, getTargetInstFunc, handleEventFunc, getTargetInstFunc The judgment here is to determine what event should be used for the current DOM node, such as the change event for the select element, That its corresponding getTargetInstFunc for getTargetInstForChangeEvent getTargetInstFunc then we call this method, the method to determine whether the event event inside the corresponding event, Such as getTargetInstForChangeEvent to determine whether the event name change, if it’s return targetInst corresponding fiber (object), and then determine whether the results returned, If there is to execute the createAndAccumulateChangeEvent create event object and returns, and so do things here because all event will remove every plugin extractEvents method, Therefore, we need to determine internally whether we need to create an Event object of the corresponding type.

CreateAndAccumulateChangeEvent this method first calls SyntheticEvent. GetPooled method to create an event object, create a way with the method of object pool, and then set the event. The type of change, Then call enqueueStateRestore and accumulateTwoPhaseDispatches last event returned.

If there are no available objects in the pool, the SyntheticEvent constructor is called to create a SyntheticEvent. This event object is a wrapper around the native event object. It implements the native object preventDefault, stopPropagation methods and adds its own methods (persist), you can get the nativeEvent object via the nativeEvent property.

EnqueueStateRestore places the target in the restoreQueue array and sets the restoreTarget to target so that its value can be restored later.

AccumulateTwoPhaseDispatches this method will be called forEachAccumulated method is introduced to the event and accumulateTwoPhaseDispatchesSingle, Actually we talked about before forEachAccumulated this method, this method is a tool, it is just to call accumulateTwoPhaseDispatchesSingle and to enter the event.

AccumulateTwoPhaseDispatchesSingle inside the method call again traverseTwoPhase incoming parameter is the fiber (targetInst), accumulateDirectionalDispatches and The event.

TraverseTwoPhase will start with the incoming fiber object and go up to all fiber nodes whose parent is HostComponent and put them in the path array. Then traverse the path of incoming callers (accumulateDirectionalDispatches), first traverse began traversal, the last element AccumulateDirectionalDispatches method was introduced into the second parameter is’ captured ‘second traversal begins with the first element traversal, passed the second parameter is the’ bubbled the two exactly the traversal sequence capture and the order of the bubble, Therefore, there is no need to determine which phase is the capture phase and which phase is the bubble phase when running the Listeners, and we can directly execute the listeners in the order of the array. By the way, the first parameter is the fiber node traversed, and the third parameter is the event object.

AccumulateDirectionalDispatches here we finally to get the event handler we set out, We first call listenerAtPhase to fetch the event handler (listener) bound to onChange or onChangeCapture, and then insert the listener into event._dispatchListeners. 参 考 答 案 Insert the corresponding fiber object into the EVENT._dispatchin参 考.

Github

Includes annotated source code, demos, and flowcharts github.com/kwzm/learn-…