React source Code grinding-test 2 contains components, Refs, and ReactChildren

preface

Front steps quickly, but we really use to have the technology content in the actual development of things really not many, many times we have to struggle with the realization of the style, waste a lot of time, also read many articles and video, bosses consistent conclusion is: the back-end more “programming”, the closer it gets to the data of the talent will be more “valuable”. The front end also wants to be the “money” guy, so we have to get into the more technical stuff, practice real programming (as opposed to typography), and try to embrace the back end. Reading and understanding the source code of some open source projects is a good way to learn and practice, but it is not easy to say, after all, the gods of the code, we do not necessarily understand.

Fortunately, we may not have to read the code all by ourselves. Moocs instructor Jocky and nuggets activist author YCK both offer courses/blogs that guide us through the source code, and we can take advantage of these excellent resources to study and take notes. I haven’t written down notes for a long time, and during the learning process, MY experience is that taking notes helps me to learn more firmly. This blog series is also based on the two gods’ sharing and their own understanding.

Two great gods information address

  • Jocky blog (free) : react.jokcy.me/
  • React source code – IMOOc
  • Yck God blog (free) : yuchengkai.cn/react/

The preparatory work

Clone a copy of the annotated source code of YCK God (version 16.8.6) or purchase the course on IMOOc to see the corresponding source code of Jocky God course (of course, if you feel that you don’t have time to watch the video, or if you are on a budget, you can study the blog first, you will learn more in this way). Of course, you should also have an official source code. Here I clone 16.9.0.

When I downloaded it, the speed was only a few K, and the highest speed was only 15K, which was good before. If you encounter similar problems, please search for solutions related to the slow download speed on Github on Baidu (add content in hosts).

  • Clone official source:git clone https://github.com/facebook/react.git
  • Clone Yckgit clone https://github.com/KieSun/react-interpretation.git

React source directory

After entering the React project and then entering the Packages directory, the code stored here is the content we need to learn. Its structure is roughly as follows.

React/Packages directory structure

├─ Create - Subscription ├─ Eslint-Plugin-react-Hooks ├─ Est - Mock - React ├─ Legacy - Events ├ ─ ─ the react ` react core packages ` ├ ─ ─ the react - art ├ ─ ─ the react - cache ├ ─ ─ the react - debug - tools ├ ─ ─ the react - devtools ├ ─ ─ the react - devtools - core ├ ─ ─ React - DevTools-Inline ├─ React -dom - Related '├─ React - Events ├─ React - Native - Renderer ├─ React-noop - Renderer ├─ React-Reconciler 'React-DOM and React-Native' ├─ React-refresh ├─ React-Stream ├─ ├─ └─ shared ├─ use-subscriptionCopy the code

We can see the react to different function encapsulation for different package, some packages can be utility by other packages, some important and independent function can also be published as a separate NPM package, we can study the modularity separation, combination and other techniques, one thousand which day we also made a fire of open source projects, little code. In 16.9.0, the directory structure is slightly different from that in 16.8.6. The event system is also changed from Events to legacy-events. We will study the specific changes in detail later. React English document 16.9.0 and Chinese document 16.8.6.

JSX and React. The createElement method

JSX has been used by JSX and JSX has been used by JSX and JSX has been used by JSX and JSX has been used by JSX.

JSX generates React elements. How does JSX generate React elements? We can find out by compiling online at Babel (click to enter)

Case 1

The JSX expression can be entered on the left, and the React code will be compiled on the right.

// Type <div id="hello" class="hello" key="hello"> hello </div>Copy the code

Enter the JSX code above, and Babel will help us compile it into the code below

/ / output
"use strict";

React.createElement("div", {
  id: "hello".class: "hello".key: "hello"
}, "Hello React");
Copy the code

React createElement() : JSX {React} JSX {React} JSX {React} JSX {React} JSX {React} JSX {React} JSX {React} JSX {React}

Just looking at the compiled code, we can also see that the currently compiled react.createElement () takes three arguments:

  1. The first is the name of the div element.
  2. The second is the attribute of the element, which is an object into which all the attributes of the element are compiled.
  3. The third is the contents of the element, currently a string.

Think about the data structure we would use to represent an HTML element, which is just the element name, attributes, and content, so these three parameters are easy to understand.

Example 2: Try writing a slightly more complex JSX expression

// Enter <ul id="hello" class="hello" key="hello"> <li> hello React</li> <li> hello JSX</li> </ul>Copy the code
/ / output
"use strict";

React.createElement("ul", {
  id: "hello".class: "hello".key: "hello"
}, React.createElement("li".null."Hello React"), React.createElement("li".null."Hello JSX"));
Copy the code

The react.createElement () function uses the same parameters as in our first example. The first two arguments are the element name and attribute, and the last two are the same parameters as in our first example.

Example 3: Write the React function component next to see what happens

Function Wrapper({children}) {return <div class=" Wrapper ">{children}</div>}< Wrapper> <ul id="hello" class="hello" key="hello"> <li>Hello React</li> <li>Hello JSX</li> </ul> </Wrapper>Copy the code
/ / output
"use strict";

function Wrapper(_ref) {
  var children = _ref.children;
  return React.createElement("div", {
    class: "wrapper"
  }, children);
}

React.createElement(Wrapper, null, React.createElement("ul", {
  id: "hello".class: "hello".key: "hello"
}, React.createElement("li".null."Hello React"), React.createElement("li".null."Hello JSX")));
Copy the code

If we change the Wrapper to lowercase Wrapper, we will compile the Wrapper variable. If we change the Wrapper to lowercase Wrapper, we will compile the Wrapper

"use strict";

function Wrapper(_ref) {
  var children = _ref.children;
  return React.createElement("div", {
    class: "wrapper"
  }, children);
}

React.createElement("wrapper".null, React.createElement("ul", {
  id: "hello".class: "hello".key: "hello"
}, React.createElement("li".null."Hello React"), React.createElement("li".null."Hello JSX")));
Copy the code

React tells us to capitalize the first letter of the component. If it’s lowercase, it compiles to a string that matches the native HTML tag, so it gets an error. Capitalize the component.

CreateElement (createElement

Through Babel’s online platform, we can see the secret behind JSX’s transition to JavaScript. You can try to see the source code: open the packages/react/SRC/react. Js (based on 16.9.0), 20 to 26 line ReactElement is introduced. The contents of the js

import {
  createElement,
  createFactory,
  cloneElement,
  isValidElement,
  jsx,
} from './ReactElement';
Copy the code

Which contains we just saw the createElement method, we can open the packages/react/SRC/createElement method. The js, locating the createElement method derived, see how the API implementation.

Create and return a new ReactElement of the given type. Indicates that the API creates and returns a new element of type ReactElement based on the given type. The API documentation is provided for those who are interested in looking at the API description and how to use it before looking at the implementation.

Snippet 1: Function definition

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

  // Reserved names are extracted
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null; . }Copy the code

Let’s start with the definition of the function and its internal variables. The three parameters of the function were guessed when we compiled with Babel:

  • typeThe: function creates different classes based on different sections of typeReactElementReact creates different react elements depending on the type of the react element. In this example, the first argument is a string with lowercase letters and the first argument is a variable with uppercase letters.
  • configThis parameter is used to describe the attribute of the element. It is an object type. Each attribute of the element can correspond to one of the elementskey-valueYes, such asid.className.key.refEtc are described in config.
  • children: represents the contents or children of the element, but the odd thing is that the function we saw earlier might have the fourth, fifth, and sixth arguments. So if you think about it, what would you do in code if you had a child to represent the third element and the next element? I think you’ve all figured it out. It worksargumentsParameter to intercept. If you were to implement it, would you do it any other way? For example, the third parameter is usedRest parameters (... children)In the function we don’t have to intercept. But why this is not used here needs to be explored and thought about.

And then some variable definitions, and you can see that even though this is JavaScript code and you can define variables at any time, the author has put the definition of variables in advance, which is also worth learning and practicing. In the process of looking at the source code, I hope you pay attention to the author’s notes as far as possible. Reserved names are extracted indicates that Reserved names are extracted. Therefore, we can guess that only some normal attributes exist in props, and special contents are filtered out, which must be followed by related logic.

Snippet 2: Element attribute filtering

  if(config ! =null) {
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    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

Here is a non-standard flow chart (roughly describe the code), if you have obsessive-compulsive students can draw a drawing to do a standard point. From the flow chart, we can clearly see the function of this code. First, filter the ref and key attributes from config, which should be reserved in the attributes, then obtain the __self and __source attributes of config, and then traverse the own attributes of config. Filter the contents of the reserved properties and store the other properties in the props object. Get the general properties and put them in props and extract the reserved properties. We can see what __self and __source look like in the definitions in lines 16-21 mentioned above.

const RESERVED_PROPS = {
  key: true.ref: true.__self: true.__source: true};Copy the code

Snippet 3: Extract children

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  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

< span style = “box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important;” The childArray object is created to store them, and the children element of the Props object points to this array. Props. Children may or may not be an array, so we need to be sure that children are not an array

The Object. Freeze () method can freeze an Object. The Object. Freeze () method can freeze a childArray Object. A frozen object can no longer be modified. If you freeze an object, you cannot add new attributes to the object, delete existing attributes, modify the enumerability, configurability, writability of existing attributes of the object, or modify the value of existing attributes. In addition, the stereotype of an object cannot be modified after it is frozen.

Snippet 4: Take the default properties

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

This code is easy to read. If there is a default attribute in type, it is iterated, and if there is no default attribute in props, it is saved. And what is the type that has defaultProps, and we saw that before, type can be a string: an HTML tag; It can also be a variable: represents a component in which some properties may exist.

Snippet 5: End

  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); }}}return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
Copy the code

There is one last bit of code left in this function. In the development environment (__DEV__), if either key or ref is present, it checks whether type is function, and if so, If it’s not function, it’s type. If it’s not function, it’s type. If it’s key or ref, And the name that’s displayed is this displayName. Finally, a ReactElement is returned based on a series of properties;

Speaking of ReactElement, let’s take a look at this API first.

ReactElement: Explore the React element

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };

  if (__DEV__) {
    // The validation flag is currently mutative. We put it on
    // an external backing store so that we can freeze the whole object.
    // This can be replaced with a WeakMap once they are implemented in
    // commonly used development environments.
    element._store = {};

    // To make comparing ReactElements easier for testing purposes, we make
    // the validation flag non-enumerable (where possible, which should
    // include every environment we run tests in), so the test framework
    // ignores it.
    Object.defineProperty(element._store, 'validated', {
      configurable: false.enumerable: false.writable: true.value: false});// self and source are DEV only properties.
    Object.defineProperty(element, '_self', {
      configurable: false.enumerable: false.writable: false.value: self,
    });
    // Two elements created in two different places should be considered
    // equal for testing purposes and therefore we hide it from enumeration.
    Object.defineProperty(element, '_source', {
      configurable: false.enumerable: false.writable: false.value: source,
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element); }}return element;
};
Copy the code

There are a lot of comments on this function. The first ones are not posted here. I hope you can take a closer look at the comments on this function. As described in the opening comment, this function is a factory method that creates React elements. It does not support the class’s schema, so it cannot use the new operator to create new elements, soinstanceofThere is no way to check its type. What if you want to see if it’s a React element? This is providedSymbol.for('react.element')In the right wayThe $typeof ‘field is checked to see if it is a React element.

Ignoring the if statement, this code creates an Element and returns it. This element contains properties such as type, key, ref, and props. Typeof: what is REACT_ELEMENT_TYPE? According to the last paragraph, we can know that it is used to determine whether an element is the element of the React, we can find it follow the definition of packages/Shared/ReactSymbols js:

export const REACT_ELEMENT_TYPE = hasSymbol
  ? Symbol.for('react.element')
  : 0xeac7;
Copy the code

I believe we can understand the logic of this code, but for Symbol. For, we usually do not use it very much, some students may be unfamiliar with it, the following quote from MDN to illustrate its function:

The symbol.for (key) method finds the corresponding Symbol from the run-time Symbol registry based on the given key key, returns it if it is found, or creates a new Symbol associated with the key and places it in the global Symbol registry.

There is also a _owner that we are not familiar with, but it is clear from the comments that it is responsible for recording the component that created the element. It tells us which component created the element.

We say element for a while, component for a while, maybe some friends will feel very dizzy, what is an element, what is a component ah?

is ReactElement(React element) and App stands for React Component.

Why did I post the if statement? Actually, I hope you can read the comment inside (very clear). If you don’t understand it, you can translate it and understand what it does. I won’t go into it here.

The summary of this chapter

  1. First we took a look at the secrets of JSX transformations using Babel. Why did we write JSX code with React instead of ReactReact.createElement()And what happens when its parameters are different.
  2. And then we got into itReact.createElement(type, config, children)The implementation of the API, by and large, doesn’t do much, filtering some of the reserved attributes, putting the general attributes inpropsIn, by rightchildrenAnd put it inprops.childrenWe must check that children are an array and return a React element.
  3. And finally we didReactElement()The React element is checked by its React element, which uses some of the parameters we dealt with in section 2 above? typeofProperty to check, it also has a_ownerProperty represents the component that created it.
  4. Finally, I left a content for you to view, I hope you can cooperate with the source code, documents, Youdao dictionary to carefully study the source code.

In the end, the content of this article is not short, I wanted to write a little more, but this article is a little long, also a little can not go on, if it is longer, readers will give up before reading, or put it in the back of the article.

React source blog: React Source blog, React source blog: React Source blog: React Source blog In fact, that’s what I thought, why not go to see the great god to see a rookie. Think about it, in fact, look at the source code, blog, everyone’s understanding and the final expression can not be the same, before also haven’t written an article for a long time, because I always feel that I want to write others have written, but also write very good, thinking that I would like to write a little bit of high quality article.

But now I feel wrong, after all, god’s idea is god, god’s expression is god, not necessarily everyone to see the extent of understanding can reach the realm of god, everyone to see the article or source code, understanding may also be different, if a rookie can speak their own ideas clearly, In fact, to a large extent, it can also help people with the same level or lower level to learn these knowledge more clearly. And reading, understanding is the input, writing is the output, in the output will think of more things.

We are also encouraged to share our learning here. If we are brave, we will write too bad, but no one will read us. At most, we will ridicule us, but what we have learned will push us forward.

This series of blogs on GitHub: github.com/kingshuaish… Due to time reasons, the update is not stable, but it will continue. I hope that there will be like-minded partners to urge each other and study together. Tomorrow, we will certainly thank ourselves for our efforts today.