0 foreword

This series of React articles is based on the latest version of the React repository, which may be slightly different from the older version.

1 Component && Pure Component

Component and PureComponent are very important to us as the entry point for all people learning React development, so this article chooses this point for analysis.

The source file is reactBaseclasses.js.

This file acts more like an abstract class, defining many of them and then providing them for external calls. The real details are not there.

1.1 Component

The base class decorator for component updates, whose main job is to initialize parameters.

The specific code is as follows:

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

Copy the code

This section describes how to initialize props, context, and updater.

The most important of these is to initialize the decorator, because react operations continue with the updater in place. We do not pass in the updater parameter during regular new builds, so it is usually initialized with the default ReactNoopUpdateQueue.

Refs emptyObject set to emptyObject by default.

1.2 PureComponent

The only difference is that the PureComponent prototype has a type identifier that determines the isPureReactComponent.

pureComponentPrototype.isPureReactComponent = true;
Copy the code

There are only two places to compare whether a ClassComponent needs to be updated in React. ShouldComponentUpdate (); shouldComponentUpdate (); shouldComponentUpdate ();

The updater will be covered in a special article.

2 React Context

The code is shown below, with the dev logic removed.

export function createContext<T> (defaultValue: T, calculateChangedBits: ? (a: T, b: T) => number,) :ReactContext<T> {
  if (calculateChangedBits === undefined) {
    calculateChangedBits = null;
  }

  const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    _threadCount: 0.Provider: (null: any),
    Consumer: (null: any),
  };

  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  context.Consumer = context;
  return context;
}

Copy the code

_currentValue: As a workaround for supporting multiple concurrent renderers, we classify some renderers as primary and others as secondary. We expect only two concurrent renderers at most: Reaction Native(primary) and Fabric(secondary); Reaction DOM(primary) and Reaction ART(secondary). The secondary renderer stores its context values in separate fields.

_threadCount: Records the number of concurrent renderers supported in the current renderer.

Provider, Consumer: These are loop calls.

Typeof: Because JSON does not support the Symbol type. For (‘ react.element ‘) is not included in JSON, even if the server returns JSON as text. React checks element. Typeof: because JSON does not support the Symbol type. So even if the server has a security vulnerability to return JSON as text, JSON does not contain symbol.for (‘react.element’). React checks element. Typeof: because JSON does not support the Symbol type. For (‘ react.element ‘) is not included in JSON, even if the server returns JSON as text. React detects element.typeof and rejects processing of the element if it is missing or invalid. The advantage of using symbol.for () specifically is that Symbols can be used in environments such as iframes and workers. Therefore, no matter how strange the conditions, this scheme does not affect the application of different parts of the transfer of trusted elements. Also, even if there are many React copies on the page, they “accept” valid $$Typeof values.

What if the browser doesn’t support Symbols?

Alas, the protection scheme would be ineffective. React still adds the $$Typeof field to ensure consistency, but just sets a number — 0xeAC7.

Why is that number? Because 0xeAC7 looks a bit like “React.”

3 React.createRef

This is an immutable object with a single mutable value. The implementation principle is also super simple.

export function createRef() :RefObject {
  const refObject = {
    current: null};return refObject;
}
Copy the code

Just create a refOject and return.

Use the dom ref to implement binding:

class App extends React.Component{
  constructor() {
    this.ref = React.createRef()
  }
  render() {
    return <div ref={this.ref} />
    // or
    return <div ref={(node)= > this.funRef = node} />}}Copy the code

4 createElement

4.1 Overview

Create an interface for the ReactElement element.

export function createElement(type, config, children) {}
Copy the code

Type: indicates the type of the node to be created.

Config: indicates the properties of the node, such as ID and class.

Children: Creates the content contained in the node. This children can be a ReactElement.

4.2 Segmented Parsing

    if (hasValidRef(config)) {
      ref = config.ref;
			Type warning in the dev environment
      if(__DEV__) { warnIfStringRefCannotBeAutoConverted(config); }}if (hasValidKey(config)) {
      key = ' ' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
Copy the code

Resolution:

Presumably parsing the passed Config object.


If config is not empty, parsing begins, with the ref attribute being the first to be resolved.

If a Key exists, bind it to the DOM to be generated.

Assign these config passes to the corresponding props.

  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];
    }
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }
Copy the code

The children object passed can have multiple arguments, and that is to assign these parameters to this.props. Children.

  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}Copy the code

If the current jsx. Element is passed in with default props, assign the default props to the currently created Element object.

Finally, call ReactElement to complete the element creation.

5 cloneElement

The steps for cloneElement are very similar to those for createElement, but there are some differences.

For example, the incoming Element is typed before clone, and an error is reported if it does not meet the criteria.

invariant( ! (element ===null || element === undefined),
    'React.cloneElement(...) : The argument must be a React element, but you passed %s.',
    element,
  );
Copy the code

The props are copied before the element is created.

const props = Object.assign({}, element.props);
Copy the code

The following operations are almost identical, such as parsing config, parsing default props, and parsing children.

6 createFactory && isValidElement

CreateFactory is used to create a factory that is specifically used to create a certain type of ReactElement, which we will barely cover.

IsValidElement is a ReactElement.

7 ReactElement

With all the preamble, the ultimate goal is to highlight the method of creating 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

After a bunch of operations on the front end, all we need to do at this point is pass in the prepared parameters for creation.

The $$typeof object is the unique representation of the React element. If it does not exist, it cannot be recognized by the React object.

_owner: Records the component responsible for creating this element (important here, more on this later).

Type, key, ref, props, etc. are passed in after the createElement process is complete.