This is the first article in my React source code tutorial. Here’s why I wrote this article:

  • React is now used in most work, so I want to know the internal mechanism
  • There are a lot of Vue source code interpretations on the market, but React source code is very difficult, so I want to tackle this problem
  • You don’t necessarily understand what you think you understand, but you really understand what you write to let readers understand, so I want to write what I understand

The React version is 16.8.6. Here are some things you should be aware of in this series:

  • This is an advanced course. If you don’t know anything about React, please Google it and have React development skills
  • This is a source code course, it is not possible to understand only reading, need to be supplemented by Demo and Debug to understand the purpose of the code
  • I fork a copy of the 16.8.6 version of the code and annotate it with detailed Chinese comments. Those of you who can’t wait for my article can read my repository first, and read along with the code I commented as you read through this series of articles. This is because different versions may lead to different code, and I will not post large chunks of code in this article. I will only explain some of the code in more detail, and the rest of the code can be read in my comments
  • One of the first problems you encounter when reading source code is not knowing where to start. This code comment can help you solve this problem. Just follow my commit to read it
  • No code is read for any DEV environment, not all code is read, only core features (and even that would be a big project)
  • Finally mentioned again, please be sure to combine the article with the code, for the sake of length I will not post all the code, I copy tired, readers read tired

This article is not going to be too hard, but it’s a good idea to open my code and navigate to the React folder SRC, which is also the react entry folder.

1. The code should be shown in pictures as much as possible, which is both beautiful and easy to read. Anyway, you don’t need to copy the code. 2. The article will only talk about the code that I think is important or interesting. For the other code, please read my warehouse by yourself, since the code has been annotated. 3. The function call with long process will be summarized in the way of flow chart. 4. Instead of just talking about code, we’ll talk about what problems these apis can help us solve.

Article related Information

  • React 16.8.6 React 16.8.6 React 16.8.6 React 16.8.6 React 16.8.6 React 16.8.6 React 16.8.6
  • Render Process (1)
  • Render Process (2)

React.createElement

You must have written JSX when writing React code, but why should React be introduced when using JSX?

This is because our JSX code will be compiled to React. CreateElement by Babel, so we can’t use React. CreateElement without React.

<div id='1'>1</div> // The JSX above will be compiled like this. CreateElement ("div", {id: "1"}, "1")Copy the code

So let’s go to ReactElement. Js and read the implementation of createElement

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

First, the createElement function takes three parameters, which you can understand from the JSX compilation above.

Then some processing for config:

This code validates the ref and key (for code that doesn’t have to read the internal implementation to know what it wants to do), then iterates through config and drops some of the built-in properties (ref and key, for example) into the props object.

The following paragraph is the operation for children

First take out the argument after the second argument, and then determine whether the length is greater than one. If it is greater than one, it means there are multiple children, in which case the props. Children is an array, otherwise it is just an object. Children is an array. You can also use the React.Children API, which will be explained in this section.

Finally, a ReactElement object is returned

The internal code is simple, the core is to pass, right? Typeof helps us recognize that this is a ReactElement, and we’ll see a lot of similar types later. Another thing to note is that
written via JSX stands for ReactElement and APP stands for React Component.

Here is the flowchart for this section:

ReactBaseClasses

Reactbaseclasses.js: ReactBaseclasses.js: ReactBaseclasses.js: ReactBaseclasses.js: ReactBaseclasses.js: ReactBaseclasses.js: ReactBaseclasses.js

In fact, before reading the source code, I thought the code would be very complicated, probably containing a lot of logic in the components, but the internal code turned out to be quite simple. This is because the React team dumped all the complex logic in the React – DOM folder. You can think of React – DOM as the glue layer between React and UI. This glue layer is compatible with many platforms, such as Web, RN, SSR, etc.

This file contains two basic components, Component and PureComponent. Let’s read the Component code first.

In the Component constructor, refs and updater are two important properties. Refs and updater are described below. SetState and forceUpdate call methods in updater. But the updater is part of the React – DOM, which we’ll learn about in a later article.

ReactNoopUpdateQueue also has a separate file, but it doesn’t matter if you look at the internal code because it’s used to report warnings.

Now let’s read the PureComponent code, which is basically the same as the Component code

PureComponent inherits from Component using a typical parasitic composition.

In addition to these two sections of code, you can see that each component has an isXXXX property that identifies what component it belongs to.

That’s the code for this section, and we’ll learn some of the refs in the next section.

Refs

Refs can be created in several ways:

  • String, but this is no longer recommended
  • ref={el => this.el = el}
  • React.createRef

In this section we will learn about React. CreateRef. The other two methods are beyond the scope of this article, so navigate to the ReactCreateref.js file.

The internal implementation is simple; if we want to use ref, we just need to fetch the current object within it.

In addition, refs cannot be used for function components, so you can read the documentation if you don’t know why.

Of course, in the past, there was also a clever way to pass ref through props, but now we have a new way of forwardRef to solve this problem.

See the forwardref.js file for details, but the internal code is still very simple

The most important thing about this code is that we can get the ref in the argument, so if we want to use ref in the function component, we can write the code like this:

const FancyButton = React.forwardRef((props, ref) = > (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
))
Copy the code

ReactChildren

This section will be the most complex part of this article, and you may need to write a Demo and Debug yourself to really understand why the source code is implemented this way.

First of all, you need to locate the reactChildren.js file. This part of the code is only about the mapChildren function, because this part of the code is basically throughout the file.

If you haven’t used the API, read the documentation for yourself.

The mapChildren function is typically used in the composite component design pattern. If you don’t know what composite components are, take a look at Ant-Design, which makes extensive use of this design pattern internally, such as radio.Group, radio.button, and there is a document that describes this design pattern.

Let’s look at some of the magic uses of this function

React.Children.map(this.props.children, c => [[c, c]])
Copy the code

For the above code, the map (mapChildren) function returns [c, c, c, c]. Regardless of how many dimensions of a nested array your second argument returns, the map function will spread you to a one-dimensional array, and the number of elements in the array returned after each iteration represents how many times the same node needs to be copied.

If the text is a bit confusing, look at the code:

<div>
    <span>1</span>
    <span>2</span>
</div>
Copy the code

For the above code, the c => [[c, c]] conversion becomes

<span>1</span>
<span>1</span>
<span>2</span>
<span>2</span>
Copy the code

So let’s get down to business and look at what’s going on inside mapChildren.

The interesting part of this code is that it introduces the concept of object reuse pools, corresponding to the code in getPooledTraverseContext and releaseTraverseContext, respectively. The idea is to maintain a pool of objects of a fixed size, assign each object from the pool, empty the properties of the object and throw it back into the pool. The whole point of maintaining this pool is to improve performance, since creating and destroying objects with many properties frequently takes a toll on performance.

Next we’ll look at the code in traverseAllChildrenImpl, which needs to be broken into two pieces

This part of the code is relatively simple, the body is trying to determine what the type of children is. If it is a node that can be rendered, call callback directly. Typeof to determine the flow. In this case, the callback is mapSingleChildIntoContext function, this part of the content will be in the following paragraphs.

This part of the code first determines whether children are arrays. If it is an array, the array is iterated over and each element in it is recursively called traverseAllChildrenImpl, which means it must be a single renderable node to execute the callback in the upper part of the code.

Iterator (obj[Symbol. Iterator]); iterator (obj[Symbol. Iterator]); iterator (obj[Symbol.

Finished traverseAllChildrenImpl function, and finally we to read mapSingleChildIntoContext function of implementation.

BookKeeping is what we pull out of the object pool, call func and pass in the node (which must be a single node at this point), where func represents the second parameter in the react. mapChildren argument.

The next step is to determine the type of return value: if it is an array, or if it is regression logic, notice that the func passed in is c => c, because the final result is amortized; If it is not an array, determine whether the returned value is a valid Element. If it is, clone it and replace the key. Finally, put the returned value into result, which is the return value of mapChildren.

So far, mapChildren functions related to the content has been resolved, is not clear students can review through the following flow chart again.

The rest of the content

The first few sections have covered most of the interesting code in the React folder, leaving a few bits and pieces behind. The react-dom folder contains all the difficult parts of the code, such as memo, context, hooks, and lazy.

List of other articles

  • Render Process (1)

The last

Reading the source code can be a tedious process, but the benefits can be huge. If you have any questions while reading, please feel free to let me know in the comments section, and of course you can mention Issus in the warehouse.

Also, writing this series is a time-consuming project, including maintaining code comments, making the article as readable as possible, and finally drawing. If you think the article looks good, please give it a thumbs up.

The next article will be about Fiber and will be divided into several articles.

Finally, feel the content is helpful can pay attention to my public number “front-end really fun”, there will be a lot of good things waiting for you.