packages/react-reconciler/src/ReactFiber.js

Fiber

// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
// Fiber is work that needs to be done or has been done on the Component. Each component can have multiple fibers.
export type Fiber = {|
  // These first fields are conceptually members of an Instance. This used to
  // be split into a separate type and intersected with the other Fiber fields,
  // but until Flow fixes its intersection bugs, we've merged them into a
  // single type.

  // These fields are conceptually members of the instance.
  // This was split into a separate type and intersected with other fiber fields,
  But we didn't merge them into a single type until Flow fixed its intersection error.

  // An Instance is shared between all versions of a component. We can easily
  // break this out into a separate object to avoid copying so much to the
  // alternate versions of the tree. We put this on a single object for now to
  // minimize the number of objects created during the initial render.
  // An instance shared between all versions of a component.
  // We can easily split it into a single object,
  // To avoid copying too much to other versions of the tree.
  // Now we put it on an object to minimize the number of objects created during the initial rendering.

  // Label the fiber type.
  tag: WorkTag,

  // The unique identifier of this child element.
  key: null | string,

  // The value of element.type which is used to preserve the identity during
  // reconciliation of this child.
  // The value of element.type, which is used to save the identity during the reconciliation of this child element.
  elementType: any,

  // The resolved function/class/ associated with this fiber.
  // The resolved function/class/ associated with this fiber.
  type: any,

  // The local state associated with this fiber.
  // The local state associated with this fiber.
  stateNode: any,

  // Conceptual aliases
  // parent : Instance -> return The parent happens to be the same as the
  // return fiber since we've merged the fiber and instance.
  // Alias of the concept
  // parent: Instance -> return
  // Parent happens to be the same as return fiber because we already merged fiber with the instance.

  // Remaining fields belong to Fiber
  // The remaining fields belong to Fiber

  // The Fiber to return to after finishing processing this one.
  // This is effectively the parent, but there can be multiple parents (two)
  // so this is only the parent of the thing we're currently processing.
  // It is conceptually the same as the return address of a stack frame.
  // This Fiber is processed and then returned.
  // This is actually parent, but there can be more than one parent (two), so this is just the parent of the thing we're currently dealing with, fiber.
  // Conceptually the same as the return address of a stack frame.
  return: Fiber | null.// Singly Linked List Tree Structure.
  // Single list tree structure.
  child: Fiber | null.sibling: Fiber | null.index: number,

  // The ref last used to attach this node.
  // Finally append the ref to this node.
  // I'll avoid adding an owner field as a function for prod and Model.
  // I'll avoid adding an owner field for prod and model that as functions.
  ref: null | (((handle: mixed) = > void) & {_stringRef: ?string}) | RefObject,

  // Input is the data coming into process this fiber. Arguments. Props.
  // Input is to process the data of this fiber. Arguments. Props.
  pendingProps: any, // This type will be more specific once we overload the tag. Once we overload the label, this type will be more specific.
  memoizedProps: any, // The props used to create the output. Props for creating the output.

  // A queue of state updates and callbacks.
  // Queue for status updates and callbacks.
  updateQueue: UpdateQueue<any> | null.// The state used to create the output
  // The state used to create the output
  memoizedState: any,

  // A linked-list of contexts that this fiber depends on
  // A list of contexts on which this fiber depends
  contextDependencies: ContextDependencyList | null.// Bitfield that describes properties about the fiber and its subtree. E.g.
  // the ConcurrentMode flag indicates whether the subtree should be async-by-
  // default. When a fiber is created, it inherits the mode of its
  // parent. Additional flags can be set at creation time, but after that the
  // value should remain unchanged throughout the fiber's lifetime, particularly
  // before its child fibers are created.
  // a bit field describing the properties of fiber and its subtrees.
  // For example, the ConcurrentMode flag indicates whether the subtree defaults to Async.
  // When a fiber is created, it inherits its parent's mode.
  // Additional flags can be set at creation time, but after that, the value should remain constant throughout the life of the fiber, especially until the sub-fiber is created.
  mode: TypeOfMode,

  // Effect
  effectTag: SideEffectTag,

  // Singly linked list fast path to the next fiber with side-effects.
  // Single linked list fast path to the next side effect fiber.
  nextEffect: Fiber | null.// The first and last fiber with side-effect within this subtree. This allows
  // us to reuse a slice of the linked list when we reuse the work done within
  // this fiber.
  // The first and last fiber in the subtree have side effects.
  // This allows us to reuse part of the list while reusing the work done in this Fiber.
  firstEffect: Fiber | null.lastEffect: Fiber | null.// Represents a time in the future by which this work should be completed.
  // Does not include work found in its subtree.
  // indicates a future time to complete the work.
  // Does not include work in subtrees.
  expirationTime: ExpirationTime,

  // This is used to quickly determine if a subtree has no pending changes.
  // This is used to quickly determine if there are no pending changes in the subtree.
  childExpirationTime: ExpirationTime,

  // This is a pooled version of a Fiber. Every fiber that gets updated will
  // eventually have a pair. There are cases when we can clean up pairs to save
  // memory if we need to.
  // This is a hybrid version of Fiber.
  // Every updated fiber will eventually have a pair.
  In some cases, we can clean it up to save memory if necessary.
  alternate: Fiber | null.// Time spent rendering this Fiber and its descendants for the current update.
  // This tells us how well the tree makes use of sCU for memoization.
  // It is reset to 0 each time we render and only updated when we don't bailout.
  // This field is only set when the enableProfilerTimer flag is enabled.
  // The time it took to render this Fiber and its descendants for the current update.
  // This tells us how the tree makes good use of sCU for memory.
  // It was reset to 0 every time it was rendered, and updated only when there was no bailout.
  // This field is set only when the enableProfilerTimer flag is enabled.actualDuration? : number,// If the Fiber is currently active in the "render" phase,
  // This marks the time at which the work began.
  // This field is only set when the enableProfilerTimer flag is enabled.
  // If Fiber is currently in the render phase,
  // This marks the time for the work to begin.
  // This field is set only when the enableProfilerTimer flag is enabled.actualStartTime? : number,// Duration of the most recent render time for this Fiber.
  // This value is not updated when we bailout for memoization purposes.
  // This field is only set when the enableProfilerTimer flag is enabled.
  // The last render duration of this fiber.
  // This value is not updated when we receive a bailout for memory purposes.
  // This field is set only when the enableProfilerTimer flag is enabled.selfBaseDuration? : number,// Sum of base times for all descedents of this Fiber.
  // This value bubbles up during the "complete" phase.
  // This field is only set when the enableProfilerTimer flag is enabled.
  // The base time of all descedents in this fiber.
  // This value appears in the "done" phase.
  // This field is set only when the enableProfilerTimer flag is enabled.treeBaseDuration? : number,// Alias of Conceptual
  // workInProgress : Fiber -> alternate
  // The alternative method for reuse happens to be the same as the ongoing work.
  // The alternative for reuse happens to be the same as for ongoing work.
  // The alternate used for reuse happens
  // to be the same as work in progress.
  // __DEV__ only_debugID? : number, _debugSource? : Source |null, _debugOwner? : Fiber |null, _debugIsCurrentlyTiming? : boolean,// Used to verify that the order of hooks does not change between renders.
  // The order used to verify that the hooks do not change between renders._debugHookTypes? :Array<HookType> | null|};Copy the code

FiberNode

function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode,) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.contextDependencies = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;

  if (enableProfilerTimer) {
    // Note: The following is done to avoid a v8 performance cliff.
    //
    // Initializing the fields below to smis and later updating them with
    // double values will cause Fibers to end up having separate shapes.
    // This behavior/bug has something to do with Object.preventExtension().
    // Fortunately this only impacts DEV builds.
    // Unfortunately it makes React unusably slow for some applications.
    // To work around this, initialize the fields below with doubles.
    //
    // Learn more about this here:
    // https://github.com/facebook/react/issues/14365
    // https://bugs.chromium.org/p/v8/issues/detail?id=8538
    this.actualDuration = Number.NaN;
    this.actualStartTime = Number.NaN;
    this.selfBaseDuration = Number.NaN;
    this.treeBaseDuration = Number.NaN;

    // It's okay to replace the initial doubles with smis after initialization.
    // This won't trigger the performance cliff mentioned above,
    // and it simplifies other profiler code (including DevTools).
    this.actualDuration = 0;
    this.actualStartTime = - 1;
    this.selfBaseDuration = 0;
    this.treeBaseDuration = 0;
  }

  if (__DEV__) {
    // do something}}Copy the code

createFiber

// This is a constructor function, rather than a POJO constructor, still
// please ensure we do the following:
// 1) Nobody should add any instance methods on this. Instance methods can be
// more difficult to predict when they get optimized and they are almost
// never inlined properly in static compilers.
// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
// always know when it is a fiber.
// 3) We might want to experiment with using numeric keys since they are easier
// to optimize in a non-JIT environment.
// 4) We can easily go from a constructor to a createFiber object literal if that
// is faster.
// 5) It should be easy to port this to a C struct and keep a C implementation
// compatible.
const createFiber = function(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode,) :Fiber {
  // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  return new FiberNode(tag, pendingProps, key, mode);
};
Copy the code

createWorkInProgress

// This is used to create an alternate fiber to do work on.
// Create standby fiber for work.
export function createWorkInProgress(current: Fiber, pendingProps: any, expirationTime: ExpirationTime,) :Fiber {
  let workInProgress = current.alternate;
  if (workInProgress === null) {
    // We use a double buffering pooling technique because we know that we'll
    // only ever need at most two versions of a tree. We pool the "other" unused
    // node that we're free to reuse. This is lazily created to avoid allocating
    // extra objects for things that are never updated. It also allow us to
    // reclaim the extra memory if needed.
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;

    if (__DEV__) {
      // DEV-only fields
      workInProgress._debugID = current._debugID;
      workInProgress._debugSource = current._debugSource;
      workInProgress._debugOwner = current._debugOwner;
      workInProgress._debugHookTypes = current._debugHookTypes;
    }

    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;

    // We already have an alternate.
    // Reset the effects TAB.
    workInProgress.effectTag = NoEffect;

    // The effect list is no longer valid.
    // The effect list is no longer valid.
    workInProgress.nextEffect = null;
    workInProgress.firstEffect = null;
    workInProgress.lastEffect = null;

    if (enableProfilerTimer) {
      // We intentionally reset, rather than copy, actualDuration & actualStartTime.
      // This prevents time from endlessly accumulating in new commits.
      // This has the downside of resetting values for different priority renders,
      // But works for yielding (the common case) and should support resuming.
      workInProgress.actualDuration = 0;
      workInProgress.actualStartTime = - 1;
    }
  }

  workInProgress.childExpirationTime = current.childExpirationTime;
  workInProgress.expirationTime = current.expirationTime;

  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;
  workInProgress.contextDependencies = current.contextDependencies;

  // These will be overridden during the parent's reconciliation
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;

  if (enableProfilerTimer) {
    workInProgress.selfBaseDuration = current.selfBaseDuration;
    workInProgress.treeBaseDuration = current.treeBaseDuration;
  }

  return workInProgress;
}
Copy the code

createHostRootFiber

export function createHostRootFiber(isConcurrent: boolean) :Fiber {
  let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;

  if (enableProfilerTimer && isDevToolsPresent) {
    // Always collect profile timings when DevTools are present.
    // This enables DevTools to start capturing Timing at any point
    // Without some nodes in the tree having empty base times.
    mode |= ProfileMode;
  }

  return createFiber(HostRoot, null.null, mode);
}
Copy the code

createFiberFromTypeAndProps

/** * Create fiber * @param type * @param key * @param pendingProps * @param owner * @param mode * @param expirationTime * @returns {Fiber|*} */
export function createFiberFromTypeAndProps(
  type: any, // React$ElementType
  key: null | string,
  pendingProps: any,
  owner: null | Fiber,
  mode: TypeOfMode,
  expirationTime: ExpirationTime,
) :Fiber {
  let fiber;

  let fiberTag = IndeterminateComponent;
  // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
  let resolvedType = type;
  if (typeof type === 'function') {
    if(shouldConstruct(type)) { fiberTag = ClassComponent; }}else if (typeof type === 'string') {
    fiberTag = HostComponent;
  } else {
    getTag: switch (type) {
      case REACT_FRAGMENT_TYPE:
        return createFiberFromFragment(
          pendingProps.children,
          mode,
          expirationTime,
          key,
        );
      case REACT_CONCURRENT_MODE_TYPE:
        return createFiberFromMode(
          pendingProps,
          mode | ConcurrentMode | StrictMode,
          expirationTime,
          key,
        );
      case REACT_STRICT_MODE_TYPE:
        return createFiberFromMode(
          pendingProps,
          mode | StrictMode,
          expirationTime,
          key,
        );
      case REACT_PROFILER_TYPE:
        return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
      case REACT_SUSPENSE_TYPE:
        return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
      default: {
        if (typeof type === 'object'&& type ! = =null) {
          switch (type.?typeof) {
            case REACT_PROVIDER_TYPE:
              fiberTag = ContextProvider;
              break getTag;
            case REACT_CONTEXT_TYPE:
              // This is a consumer
              fiberTag = ContextConsumer;
              break getTag;
            case REACT_FORWARD_REF_TYPE:
              fiberTag = ForwardRef;
              break getTag;
            case REACT_MEMO_TYPE:
              fiberTag = MemoComponent;
              break getTag;
            case REACT_LAZY_TYPE:
              fiberTag = LazyComponent;
              resolvedType = null;
              breakgetTag; }}let info = ' ';
        if (__DEV__) {
          if (
            type === undefined| | -typeof type === 'object'&& type ! = =null &&
              Object.keys(type).length === 0)
          ) {
            info +=
              ' You likely forgot to export your component from the file ' +
              "it's defined in, or you might have mixed up default and " +
              'named imports.';
          }
          const ownerName = owner ? getComponentName(owner.type) : null;
          if (ownerName) {
            info += '\n\nCheck the render method of `' + ownerName + '`.';
          }
        }
        invariant(
          false.'Element type is invalid: expected a string (for built-in ' +
            'components) or a class/function (for composite components) ' +
            'but got: %s.%s',
          type == null ? type : typeof type,
          info,
        );
      }
    }
  }

  fiber = createFiber(fiberTag, pendingProps, key, mode);
  fiber.elementType = type;
  fiber.type = resolvedType;
  fiber.expirationTime = expirationTime;

  return fiber;
}
Copy the code

createFiberFromElement

/** * create fiber * @param Element * @param mode * @param expirationTime * @returns {fiber} */
export function createFiberFromElement(element: ReactElement, mode: TypeOfMode, expirationTime: ExpirationTime,) :Fiber {
  let owner = null;
  if (__DEV__) {
    owner = element._owner;
  }
  const type = element.type;
  const key = element.key;
  const pendingProps = element.props;
  const fiber = createFiberFromTypeAndProps(
    type,
    key,
    pendingProps,
    owner,
    mode,
    expirationTime,
  );
  if (__DEV__) {
    fiber._debugSource = element._source;
    fiber._debugOwner = element._owner;
  }
  return fiber;
}
Copy the code

createFiberFromFragment


/** * Create Fiber * @param elements * @param mode * @param expirationTime * @param key * @returns {Fiber} */
export function createFiberFromFragment(elements: ReactFragment, mode: TypeOfMode, expirationTime: ExpirationTime, key: null | string,) :Fiber {
  const fiber = createFiber(Fragment, elements, key, mode);
  fiber.expirationTime = expirationTime;
  return fiber;
}
Copy the code

createFiberFromProfiler

/** * create Fiber * @param pendingProps * @param mode * @param expirationTime * @param key * @return {Fiber} * /
function createFiberFromProfiler(pendingProps: any, mode: TypeOfMode, expirationTime: ExpirationTime, key: null | string,) :Fiber {
  if (__DEV__) {
    if (
      typeofpendingProps.id ! = ='string' ||
      typeofpendingProps.onRender ! = ='function'
    ) {
      warningWithoutStack(
        false.'Profiler must specify an "id" string and "onRender" function as props',); }}const fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
  // TODO: The Profiler fiber shouldn't have a type. It has a tag.
  fiber.elementType = REACT_PROFILER_TYPE;
  fiber.type = REACT_PROFILER_TYPE;
  fiber.expirationTime = expirationTime;

  return fiber;
}
Copy the code

createFiberFromMode

Fiber * @param pendingProps * @param mode * @param expirationTime * @param key * @param RETURNS {fiber} */
function createFiberFromMode(pendingProps: any, mode: TypeOfMode, expirationTime: ExpirationTime, key: null | string,) :Fiber {
  const fiber = createFiber(Mode, pendingProps, key, mode);

  // TODO: The Mode fiber shouldn't have a type. It has a tag.
  const type =
    (mode & ConcurrentMode) === NoContext
      ? REACT_STRICT_MODE_TYPE
      : REACT_CONCURRENT_MODE_TYPE;
  fiber.elementType = type;
  fiber.type = type;

  fiber.expirationTime = expirationTime;
  return fiber;
}
Copy the code

createFiberFromSuspense

** * @param props * @param mode * @param expirationTime * @param key * @returns {fiber} * /
export function createFiberFromSuspense(pendingProps: any, mode: TypeOfMode, expirationTime: ExpirationTime, key: null | string,) {
  const fiber = createFiber(SuspenseComponent, pendingProps, key, mode);

  // TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
  const type = REACT_SUSPENSE_TYPE;
  fiber.elementType = type;
  fiber.type = type;

  fiber.expirationTime = expirationTime;
  return fiber;
}
Copy the code

createFiberFromText

/** * @param content * @param mode * @param expirationTime * @returns {fiber} */
export function createFiberFromText(content: string, mode: TypeOfMode, expirationTime: ExpirationTime,) :Fiber {
  const fiber = createFiber(HostText, content, null, mode);
  fiber.expirationTime = expirationTime;
  return fiber;
}
Copy the code

createFiberFromHostInstanceForDeletion

export function createFiberFromHostInstanceForDeletion() :Fiber {
  const fiber = createFiber(HostComponent, null.null, NoContext);
  // TODO: These should not need a type.
  fiber.elementType = 'DELETED';
  fiber.type = 'DELETED';
  return fiber;
}
Copy the code

createFiberFromPortal


Fiber * @param Portal * @param mode * @param expirationTime * @returns {fiber} */
export function createFiberFromPortal(portal: ReactPortal, mode: TypeOfMode, expirationTime: ExpirationTime,) :Fiber {
  constpendingProps = portal.children ! = =null ? portal.children : [];
  const fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
  fiber.expirationTime = expirationTime;
  fiber.stateNode = {
    containerInfo: portal.containerInfo,
    pendingChildren: null.// Used by persistent updates
    implementation: portal.implementation,
  };
  return fiber;
}
Copy the code