Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”. This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

React17 React17 React17 React17 React17 React17 React17 React17 React17

From this chapter, we will learn the React source code. This chapter includes the following contents:

  • JSX compilation is different before and after REact17
  • The React. The createElement method source code
  • React.Com ponent source

JSX conversion

Use the React app to create a simple Hello, World application:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class App extends Component {
  render() {
    return <div>hello, world</div>;
  }
}

ReactDOM.render(<App />.document.getElementById('root'));
Copy the code

Return

Hello, world

JSX {return

hello, world

JSX}}

Import react, {Component} from ‘react’. React doesn’t seem to be used anywhere in the code. Why introduce react?

16. Version X and before

We tried to remove the React reference in react16.8:

// import React, { Component } from 'react';
import { Component } from 'react'; // Remove the React reference
import ReactDOM from 'react-dom';

export default class App extends Component {
  render() {
    return <div>hello, world</div>;
  }
}

ReactDOM.render(<App />.document.getElementById('root'));
Copy the code

‘React’ must be in scope when using JSX error:

This is because the JSX syntax of

hello, world

is returned in render. In version 16 and before, The application converts JSX syntax to react. CreateElement’s JS code via @babel/preset- React, so React needs to be explicitly introduced in order for createElement to be called normally. We can see the result of JSX being compiled by @babel/preset-react in Babel REPL:

17. Version X and later

After the release of Act17, the official partnership with Babel, directly through the willreact/jsx-runtimeNew conversions to JSX syntax without dependenciesReact.createElementThe result of the conversion is direct supplyReactDOM.renderThe ReactElement object used. So if you only use JSX syntax after React17 and don’t use other react apis, you don’t need to introduce itReact, the application will still run normally.

The JSX syntax in ACT17 is compiled as follows:

To learn more about the React JSX conversion, visit the React JSX conversion website: here we introduce the new JSX conversion.

The React. The createElement method source code

Although we can no longer rely on the React.createElement API after React17, there are many scenarios in which elements are created manually using react. createElement and many open source packages. React.createElement is recommended.

React.createElement takes three or more arguments:

  • Type: The React element type to be created. It can be a tag name string, such as'div'or'span'And so on; It can also be of the React component type (class or function component); Or React Fragment type.
  • Config: a collection of attributes written to the tag in js object format. Null if no attributes are added to the tag.
  • Children: The arguments after the third argument are the children of the React element. Each argument is of a type. If the current element’s textContent is a string. Otherwise the element created for the new React. CreateElement.

A series of parses are performed on the parameters in the function. The source code is as follows, and the interpretation of the source code is marked with comments:

export function createElement(type, config, children) {
  let propName;

  // Record the collection of attributes on the tag
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  // If config is not null, there is an attribute on the tag. Add the attribute to props
  // Special properties for react provided by key and ref are not added to props, but are recorded separately with key and ref
  if(config ! =null) {
    if (hasValidRef(config)) {
      // if there is a valid ref, the ref is assigned
      ref = config.ref;
      
      if(__DEV__) { warnIfStringRefCannotBeAutoConverted(config); }}if (hasValidKey(config)) {
      // If there is a valid key, the key is assigned a value
      key = ' ' + config.key;
    }

    // Self and source record the position of the code in the compiler for debugging in the development environment
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Add attributes in config other than key, ref, __self, and __source to props
    for (propName in config) {
      if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; }}}// Add the child node to the children property of props
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    // If there are three parameters, only one child is assigned to the children property of the props
    props.children = children;
  } else if (childrenLength > 1) {
    // Push the child node to an array and assign the array to the children of props
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    // Freeze childArray in the development environment to prevent arbitrary modification
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }

  // If there is defaultProps, traverse it and add an attribute to the props that the user did not set manually on the tag
  // This is for the class component type
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}}// Key and ref are not mounted to props
  // In a development environment, if you want to obtain this parameter through props. Key or props
  if (__DEV__) {
    if (key || ref) {
      const displayName =
        typeof type === 'function'
          ? type.displayName || type.name || 'Unknown'
          : type;
      if (key) {
        defineKeyPropWarningGetter(props, displayName);
      }
      if(ref) { defineRefPropWarningGetter(props, displayName); }}}// Call ReactElement and return
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}
Copy the code

The react. createElement does the following:

  • Parse whether the config parameter has valid key, ref, __source, and __self attributes, and if so, assign keys, ref, source, and self, respectively; Mount the remaining properties resolution to the props
  • The following parameters, except type and config, are mounted toprops.children
  • For class components, if type.defaultProps exists, traverse the property of type.defaultProps. If the property does not exist, add it to the props
  • Call type, key, ref, self, and propsReactElementFunction to create a virtual DOM,ReactElementMainly through the development environmentObject.definePropertyMake _store, _self, _source non-enumerable to improve element comparison performance:
    const ReactElement = function(type, key, ref, self, source, owner, props) {
      const element = {
        // Indicates whether it is ReactElement
        $$typeof: REACT_ELEMENT_TYPE,
    
        // The information used to create the real DOM
        type: type,
        key: key,
        ref: ref,
        props: props,
    
        _owner: owner,
      };
    
      if (__DEV__) {
        element._store = {};
    
        // Make _store, _self, _source non-enumerable in development environment to improve element comparison performance
        Object.defineProperty(element._store, 'validated', {
          configurable: false.enumerable: false.writable: true.value: false});Object.defineProperty(element, '_self', {
          configurable: false.enumerable: false.writable: false.value: self,
        });
    
        Object.defineProperty(element, '_source', {
          configurable: false.enumerable: false.writable: false.value: source,
        });
        // Freeze elements and props to prevent manual modification
        if (Object.freeze) {
          Object.freeze(element.props);
          Object.freeze(element); }}return element;
    };
    Copy the code

So here’s what createElement does:

React.Com ponent source

We went back to the hello, world application code, create the class Component, we inherited from the Component, introduced the react repository, we look at the React.Com ponent source code:

function Component(props, context, updater) {
  // initialize the props, context, updater, and mount it to this
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // isMounted, enqueueForceUpdate, enqueueSetState, and other trigger methods are mounted on the updater
  this.updater = updater || ReactNoopUpdateQueue;
}

// The isReactComponent is mounted on the prototype chain to distinguish it from function components when reactdom.render is used
Component.prototype.isReactComponent = {};

// Add the 'this.setState' method to the class component
Component.prototype.setState = function(partialState, callback) {
  // Verify that the parameters are valid
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null
  );
  // Add to enqueueSetState queue
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

// Add the 'this.forceUpdate' method to the class component
Component.prototype.forceUpdate = function(callback) {
  // Add to enqueueForceUpdate queue
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
Copy the code

React.Component does the following things:

  • Mount props, context, and updater on this
  • Add an isReactComponent object to the Component prototype chain to mark class components
  • Add to the Component prototype chainsetStatemethods
  • Add to the Component prototype chainforceUpdatemethods

So we understand the function of super() of the React class component and the origin of this.setstate and this.forceUpdate

conclusion

This chapter describes the different JSX conversions before and after Act17. In fact, Babel’s JSX conversions after Act17 are one step more than beforeReact.createElementThe action:

React.createelement and react.componentare implemented internally. With Babel and React. CreateElement, JSX is converted to a native JS syntax that browsers can recognize, paving the way for React to state changes, event responses, and page updates.

Later chapters will explore how React renders state information into a real page step by step.