purpose

What is Fiber doing in setState by looking at the call stack and other blogs

Create a demo with create-react-app

Here is a typical create-react-app creation project, where text.js is my new child component referenced in app.js.

2. Modify app. js file and add text. js file

App. Js:

import React, { Component } from 'react';
import { Text } from './Text'

class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            tab: 'Welcome to React'
        }
    }
    updateState = () => {
        this.setState(() => ({tab: 'Bye Bye'}}))render() {
    return (
      <div className="App"> <Text tab={this.state.tab} updateState={this.updateState} /> </div> ); }}export default App;

Copy the code

Text. Js:

import React from 'react'

export const Text = (props) => {
    return (
        <span onClick={() => props.updateState()}>{props.tab}</span>
    )
}
Copy the code

3. Run setState

The initial value of state. TAB is ‘Welcome to React’. In setState, an arrow function is passed to update state. TAB to ‘Bye Bye’,

//partialState is () => ({TAB:' 'Bye Bye}), the callback no incoming Component. The prototype. SetState =function(partialState, callback) {/ / this is the current instance of a component. This updater. EnqueueSetState (this, partialState, callback,'setState');
};
Copy the code

In the current App component instance, _reactInternalFiber is the fiber of the current component. In the current App component instance, _reactInternalFiber is the fiber of the current component. And _reactInternalInstance is the object used in Act15.

4. Updater

The updater has three methods and only cares about enqueueSetState

var updater = { isMounted: isMounted, /* * instance: The App component instance passed in from the previous step, * partialState: the updated arrow function, * callback: undefined */ enqueueSetState:function(instance, partialState, callback) {// Get (instance); / / calculate the current fiber maturity (priority) var expirationTime = computeExpirationForFiber (fiber); Var update = {expirationTime: expirationTime, // Priority partialState: Callback: callback, // isReplace:false, //
        isForced: false, // Whether to force update capturedValue: null, // next: null, //}; InsertUpdateIntoFiber (fiber, update); // Fiber task scheduleWork(Fiber, expirationTime); }, // replace update state with enqueueReplaceState:function(instance, state, callback) {}, // Perform mandatory update state, ignoring enqueueForceUpdate:function (instance, callback) {}
  };
Copy the code

Here’s a step-by-step look at the execution flow inside this function.

Get fiber: key === instance. Fiber is important because it records a lot of useful information, such as the various properties and states of the current component instance, priority, identity, and so on.

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

In case you’re wondering what Fiber looks like, here is the injected Fiber data structure on the component instance.

Calculate expiration time: that is, calculate the priority of the current Fiber task. From the perspective of the code, there are many conditions to determine, which can be asynchronous update or synchronous update. In the current test, you are entering a synchronous update process. Synchronization has a priority of 1, so expirationTime = 1.

// This is used to calculate the expiration time of fiber, which is used to indicate the priority of the task.function computeExpirationForFiber(fiber) {
    var expirationTime = void 0;
    if(expirationContext ! == NoWork) { // expirationTime = expirationContext; }else if (isWorking) {
      if(isresearch) {// Synchronize mode, do the task immediately, default is 1 expirationTime = Sync; }else{// Updates in the render phase should expire at the same time as the work being rendered. expirationTime = nextRenderExpirationTime; }}else{// Create an expiration date if there is no expiration dateif (fiber.mode & AsyncMode) {
        if(isBatchingInteractiveUpdates) {/ / it is an interactive update var currentTime = recalculateCurrentTime (); expirationTime = computeInteractiveExpiration(currentTime); }else{// This is an asynchronous update var _currentTime = recalculateCurrentTime(); expirationTime = computeAsyncExpiration(_currentTime); }}else{// This is a synchronization update expirationTime = Sync; }}if(isBatchingInteractiveUpdates) {/ / it is an interactive update. Track the minimum wait interaction expiration time. This allows us to synchronously refresh all interactive updates as needed.if(lowestPendingInteractiveExpirationTime === NoWork || expirationTime > lowestPendingInteractiveExpirationTime) { lowestPendingInteractiveExpirationTime = expirationTime; }}return expirationTime;
  }
Copy the code

Add the update information to Fiber: This function is used to update the update information from the above calculation to fiber.

functionInsertUpdateIntoFiber (fiber, update) {insertUpdateIntoFiber(fiber, update) {insertUpdateIntoFiber(fiber, update) {ensureUpdateQueues(fiber); Queue1 and queue2 are local variables that hold queue information. Queue1 and queue2 are local variables that hold queue information. var queue1 = q1; var queue2 = q2; // If there is only one queue, add the update to the queue and exit.if (queue2 === null) {
    insertUpdateIntoQueue(queue1, update);
    return; } // If either queue is empty, we need to add to both queues.if(queue1. Last = = = null | | queue2. Last = = = null) {/ / will update the value of the update to the queue and queue 1, 2, and then exit the function insertUpdateIntoQueue (queue1, update); insertUpdateIntoQueue(queue2, update);return; } // If neither list is empty, the last update to both lists is the same because the structure is shared. So, we should just append to one of the lists. insertUpdateIntoQueue(queue1, update); // But we still need to update queue2's last pointer. queue2.last = update; }Copy the code

When initialized, the updateQueue in fiber is null, creating a createUpdateQueue updateQueue. Alternate is essentially fiber, which records the last setState operation of fiber, and alternate is a property of Fiber.

EnsureUpdateQueues ensures that the update queue is not null.

var q1 = void 0;
var q2 = void 0;
functionensureUpdateQueues(fiber) { q1 = q2 = null; // We will have at least one and at most two different update queues. //alternate is a property on fiber, initialization is null, executesetState saves the current FiberNode to alternate next timesetState, you can read it, and you can use it to do State rollback. var alternateFiber = fiber.alternate; var queue1 = fiber.updateQueue;ifQueue1 = fiber. UpdateQueue = createUpdateQueue(null); queue1 = fiber. } var queue2 = void 0;if(alternateFiber ! == null) { queue2 = alternateFiber.updateQueue;if(queue2 === null) { queue2 = alternateFiber.updateQueue = createUpdateQueue(null); }}else{ queue2 = null; } queue2 = queue2 ! == queue1 ? queue2 : null; // use the module variable q1 = queue1; q2 = queue2; }Copy the code
// Update the value of update to queue.functionInsertUpdateIntoQueue (queue, update) {// Append updates to the end of the list.ifQueue. First = queue. Last = update; }else {
    queue.last.next = update;
    queue.last = update;
  }
  if(queue.expirationTime === NoWork || queue.expirationTime > update.expirationTime) { queue.expirationTime = update.expirationTime; }}Copy the code

ScheduleWork: Remember what you did in the above steps? Get the fiber on the component instance, then calculate the priority and other fiber properties that need to be updated, and finally update the fiber to create the update queue. React doesn’t work yet, doesn’t it? Update queues are in place, fibre is in place, and React’s brain needs to schedule Fiber.

The scheduling logic is complicated because there are too many factors for me to list them all, and I can only identify the parts that are used based on the current call stack.

// We pass in two parameters, fiber and priority. ScheduleWorkImpl is the logical part of the function.function scheduleWork(fiber, expirationTime) {
    return scheduleWorkImpl(fiber, expirationTime, false);
  }
Copy the code

Note that the current incoming fiber is the fiber with the update attribute incorporated.

The recordScheduleWorkImpl function is used to determine whether an update is being submitted or is in progress. The recordScheduleWorkImpl function is used to determine whether an update is being submitted or is in progress. Some conditional presets to use.

Then set node = fiber, and execute the loop. The current node (fiber) is not empty, depending on the condition, empty the node during the loop and exit the function. So what does this emptying process do?

First, check whether the expiration time in node is equal to NoWork, which stands for 0 and represents fiber that is not currently scheduled. Then check whether the expiration time of node is greater than the incoming expiration time. If the condition is met, update the expiration time of node to the new incoming expiration time.

If alternate is not null, alternate will be null when setState has not been executed, usually when it is initialized. When setState has been executed once, old FiberNode is assigned to Alternate. In the following function, If alternate is not empty, and the context of expirationTime and the last if judgment is consistent, then update alternate in expirationTime.

The third condition is to check whether return is null. The meaning of return is described in fully Understanding Fiber. In this demo, the current is the first execution and all its returns are null.

functionScheduleWorkImpl (Fiber, expirationTime, isErrorRecovery) {recordScheduleUpdate(); var node = fiber;while(node ! == null) {// Move the parent path to the root directory and update the expiration time for each node.if (node.expirationTime === NoWork || node.expirationTime > expirationTime) {
        node.expirationTime = expirationTime;
      }
      if(node.alternate ! == null) {if(node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) { node.alternate.expirationTime = expirationTime; }}if (node['return'] === null) {
        if (node.tag === HostRoot) {
          var root = node.stateNode;
          if(! isWorking && nextRenderExpirationTime ! == NoWork && expirationTime < nextRenderExpirationTime) {// is an interrupt. (For performance tracing.) interruptedBy = fiber; resetStack(); }if(// If we are in the render phase, we do not need to arrange the root directory for this update, as we will do this before exiting. ! isWorking || isCommitting || // ...... Unless this is a different root than the one we rendered. nextRoot ! == root) {// Add root to the root dispatch requestWork(root, expirationTime); }}else {
          return;
        }
      }
      node = node['return']; }}Copy the code
// As the name implies, record the scheduling statusfunction recordScheduleUpdate() {
  if (enableUserTimingAPI) {
    if(isresearch) {// Do you have a scheduling task committing (isresearch) {// Do you have a scheduling task committing (isresearch) { hasScheduledUpdateInCurrentCommit =true; } //currentPhase indicates which lifecycle is currently executed.if(currentPhase ! == null && currentPhase ! = ='componentWillMount'&& currentPhase ! = ='componentWillReceiveProps') {/ / if there is a current task scheduling to a certain stage of life cycle hasScheduledUpdateInCurrentPhase =true; }}}Copy the code

At this point, updater’s function is done, so let’s summarize what it does. There are four points:

  • Find Fiber on the instance
  • Calculate the current fiber priority
  • The fiber to be updated is pushed to the update queue
  • The update work is determined according to the priority of the Fiber tree. The recursion starts from the return of the current fiber until the root node is reached, and the return of the root node is null.

subsequent

According to the call stack, we saw the execution process of the setState function, but we did not see the update on the browser at this time, because the specific scheduling work still depends on the core algorithm of React. Updater only updated fiber into the queue and determined the update priority.

React event synthesis, Diff algorithm, virtual DOM parsing, life cycle execution, etc. So much code that you could write a book explaining it all.

If you have time later, you can talk about how setState () => ({TAB: ‘Bye Bye’}) is updated. CreateWorkInProgress (Current, pendingProps, expirationTime)

No matter which domestic god’s blog, as long as it is to introduce fiber, all changes do not leave its case, look at this article abroad: Fiber detailed explanation

Bye, Bye, everybody.

Oh, my God. What is this