Concept paper

There are a lot of concepts in React. The most common ones we deal with are JSX, virtual DOM, Fiber, React Compenent, React Element, etc. But we may never have a deep understanding of the relationship between them?

  • Are JSX and Fiber the same thing?
  • Are JSX and the virtual DOM the same thing?
  • React Compenent, React Element what is the relationship between them?

Let’s start with these basic concepts in this chapter.

JSX

As React developers we use JSX a lot. If you’re not familiar with JSX, please read the description on the React website. JSX is compiled to React. CreateElement when compiled with Babel

function App() {
    // JSX
    return (
        <div>
        <p>Hellp world</p>
        </div>
    );
}

function App() {
    // react.createElement
    return React.createElement("div".null, React.createElement("p".null."You clicked ", count, " times"));
}
Copy the code

This is why every file must be realistically imported before React17. Otherwise, the React variable will be undefined at runtime. There was no need to display React after React17.

JSX will eventually compile to react. CreateElement. What does react. CreateElement do?

react.createElement

export function createElement(type, config, children) {
  let propName;
  
  const props = {};

  let key = null;
  let ref = null;
  // Can be ignored
  let self = null;
  let source = null;

  if(config ! =null) {
    if (hasValidRef(config)) {
      ref = config.ref;

      if(__DEV__) { warnIfStringRefCannotBeAutoConverted(config); }}if (hasValidKey(config)) {
      key = ' ' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    for (propName in config) {
      if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; }}}const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}}return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}
Copy the code
  • Type: There are three types: string, function, and class.
    • String represents the DOM of the current type, such as ‘div’.
    • Function: represents function component
    • Class: indicates class compnent
  • Config: JSX binding property
  • Children: Children in JSX

The react.createElement function does a few things:

  • Extract key and ref from config for separate processing
  • Assign the properties of config to props except key, ref, self, and source
  • If type (typically class Component) has the defaultProps property, handle the defaultProps property
  • Call ReactElement

From the above code, we can see that react.createElement will eventually call ReactElement.

ReactElement

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };
  return element;
};
Copy the code

ReactElement returns an object whose $$typeof is assigned to REACT_ELEMENT_TYPE. We all know that React has a function called isValidElement to check whether it is a Reacrt Element:

export function isValidElement(object) {
  return (
    typeof object === 'object'&& object ! = =null &&
    object.$$typeof === REACT_ELEMENT_TYPE
  );
}
Copy the code

Object.$$typeof === REACT_ELEMENT_TYPE {React Element} React Elements are the result of the JSX runtime, which means that every JSX will eventually be converted to React Elements.

What about React Compenent?

React Compenent

React has two types of Compenent: ClassCompenent and FunctionCompenet. Including ClassCompenent inheritance React.Com ponent/React. PureComponent, so now let’s look at two objects:

// Component
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null.'setState(...) : takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',);this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
// PureComponent
function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
During the update phase, isPureReactComponent is true, and if true, props and state are compared to determine whether to update
pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent};
Copy the code

The Component object contains only props, context, refs, updater, isReactComponent, which is used to determine whether it is ClassCompenent. And setState and forceUpdate functions to trigger updates. The PureComponent object, on the other hand, inherits Component with an isPureReactComponent property. This property, if true, compares props and state to determine whether the Component is updated before Compenent updates it.

if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return(! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState)); }Copy the code

React. createElement we know that both ClassCompenent and FunctionCompenent will eventually be mounted to the type of the ReactElement object, forming the following object:

{
  $$typeof: Symbol(react.element),
  key: null.props: {},
  ref: null.type: ƒ App (),_owner: null._store: {validated: false},
  _self: null._source: null 
}
Copy the code

The React Element does not contain schedule, schedule, and render information, which should be stored in Fiber.

Fiber

export type Fiber = {
  // The current Fiber type, such as FunctionComponent
  tag: WorkTag
	// key
  key: null | string.ClassComponent FunctionCompoent element.tagName, which is the same type as the following
  elementType: any.// Same as elementType above, but with LazyComponent, type=null
  type: any.// satteNode has four cases
  // 1. functionCoponent stateNode = null
  // 2. classComponent stateNode = instance
  // 3. The stateNode is the current Dom instance
  // 4. rootFiber stateNode = fiberRoot
  stateNode: any.// parent Fibers
  return: Fiber | null.// The first child of the current fiber
  child: Fiber | null.// Sibling of the current node
  sibling: Fiber | null.// The location of the current node
  index: number.// ref
  ref:
    | null
    | (((handle: mixed) = > void) & {_stringRef:?string. }) | RefObject,// Props to and from the parent node
  / / object
  pendingProps: any.// Save this update for next update and assign after beiginWork is complete
  memoizedProps: any.// Attribute update queue
  updateQueue: mixed,

  // Save this status
  memoizedState: any.dependencies: Dependencies | null./** NoMode = 0b00000; StrictMode = 0b00001; BlockingMode = 0b00010; ConcurrentMode = 0b00100; ProfileMode = 0b01000; DebugTracingMode = 0b10000; * * /
  mode: TypeOfMode,
  / / Effect
  flags: Flags,
  subtreeFlags: Flags,
  deletions: Array<Fiber> | null.// Next effect
  nextEffect: Fiber | null.// The first effect of this update
  firstEffect: Fiber | null.// Update the last effec
  lastEffect: Fiber | null.// Priority of the Lane model
  lanes: Lanes,                       
  childLanes: Lanes,
  // In a double cache, a node points to another tree
  alternate: Fiber | null.// The time taken to render the current node and its descendants is calculated only when enableProfilerTimer is enabledactualDuration? :number.// indicates the time to start renderingactualStartTime? :number.// The render timeselfBaseDuration? :number.// Total time, calculated in commite phasetreeBaseDuration? :number.// Conceptual aliases
  // workInProgress : Fiber -> alternate 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, _debugNeedsRemount? :boolean.// Used to verify that the order of hooks does not change between renders._debugHookTypes? :Array<HookType> | null|};Copy the code

React In the mount phase, the JSX object is calculated as Fiber object and stored in The pendingProps. After beignWork, the JSX object is also stored in memoizedProps and exported to the child nodes.

When Reconciler is updated, the attributes on the JSX and Fiber are compared to determine whether the current node needs to be updated. The comparison result is marked on the flag.

Now that we know the main data structures in the render phase, the next chapter will begin in earnest

Part of this article is react.iamkasong.com/

React source code