preface

As the beginning of this series, this series is an extranet blog that I found when I wanted to learn React source code. All the content is compiled according to the Extranet blog. Here is the extranet link. Before that, it needs to be explained that what is compiled here can only be used as a stepping stone to read React source code. It provides you with the most basic implementation ideas in React, which is a miniature implementation of castration version. It does not involve performance optimization, and there are certain differences between specific implementation behaviors and React. For the future really learn React source base, skilled React and want to in-depth React source code personal recommendation ode big guy React technology reveal, but the novice village players and dyslexia patients will be crazy to discourage (that’s me), here is a happy novice guide, I think roughly understand here to go deep, The learning slope will not be so steep;

Learning step

There are 8 steps to implementing a mini version of react:

  • Step one: The createElement Function
  • Step 2: The render Function
  • Step 3: Concurrent Mode
  • Step four: Fibers
  • Render and Commit Phases
  • Step 6: Reconciliation
  • Step 7: Function Components
  • Step 8: Hooks

I will organize the article in two steps in a group to avoid too much space and make the connection between the top and bottom less trivial. After finishing all the content, I will upload the final code to GitHub. Here I will dig a pit first.

This chapter is mainly about the implementation of createElement and Render

Based on the cognitive

First, let’s look at some basic React code

const element = <h1 title="foo">Hello</h1>
const container = document.getElementById("root")
ReactDOM.render(element, container)
Copy the code

This is basic React code that uses JSX to create an H1 tag and then mount it for rendering

Let’s take a look at the general process during this period:

We all know that JSX of React depends on Babel for transformation, so that it can be converted into normal JS code for use. Here we don’t care how Babel is transformed, we only discuss the converted code

The code snippet above

const element = <h1 title="foo">Hello</h1>
Copy the code

After the transformation of Babel

const element = React.createElement("h1", { title: "foo" }, "Hello")
Copy the code

At this point you can print the Element in the console

This is the object that JSX has converted to create from React. CreateElement. After that, we’ll throw away the other properties, focusing on type and props. Props specifies the properties on the node and the child elements of the node. (Note that all objects created from react. CreateElement in this article are called react elements, and those created from native DOM are called DOM nodes.)

As we know, JSX uses Babel to convert the written tag to a function call called React. CreateElement, which returns a React element

Reactdom.render () ¶ Reactdom.render () ¶ Reactdom.render ()

const element = {
  type: "h1".props: {
    title: "foo".children: "Hello",}}const node = document.createElement(element.type)
node.title = element.props.title
const text = document.createTextNode("")
text.nodeValue = element.props.children
node.appendChild(text)
const app = document.querySelector("#root")
app.appendChild(node)
Copy the code

See, we implemented this simple function without reactdom. render. Now that you know the general flow of execution in the instance code, let’s start by implementing a createElement

Again, in the React. CreateElement implementation we only care about the type and props properties

Let’s look at an example

/ / JSX syntax
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
/ / the Babel after conversion
const element = React.createElement(
  "div",
  { id: "foo" },
  React.createElement("a".null."bar"),
  React.createElement("b"))// React element (props, props);
{
  type:"div".props: {id:"foo".children:[
      {
        type:"a".props: {children: ["bar"]}}, {type:"b".props: {children:[]},}]}}// This createElement function can be implemented as follows
function createElement(type,props,... children){
  return{
    type,
    props: {... props, children } } }// We will default to an empty array of properties when there are no children. We will also default to an array of properties when there is only text in the element. React performance and real React performance may be different, the last tip, this article all implementation, in order to realize the function as the premise does not involve the specific React performance and real performance, is castricted simplified version only for you to provide ideas
Copy the code

We found that the React children array can contain not only React created elements, but also raw values (numbers, strings), for which we created a separate type, TEXT_ELEMENT

What you create with TEXT_ELEMENT is actually the corresponding text node, and here is the function it creates

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT".props: {
      nodeValue: text,
      chilren: [],},}}// Then change the original implementation of createElement
function createElement(type, props, ... children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) = > {
        return typeof child === "object" ? child : createTextElement(child)//+-,}}}}),Copy the code

Ok, now that we’ve simply implemented createElement, let’s give our library a name Didact (emmm… I copied the name from the original blog.)

Now you can experiment and see if the printouts are exactly what you think:

const Didact = { createElement }
const element = Didact.creatElement(
  "div",
  { id: "foo" },
  Didact.creatElement("a".null."bar"),
  Didact.creatElement("b"))Copy the code

render

As mentioned earlier, the main function of the Render function is to convert the React element into a real DOM and mount it to the specified node

Now let’s implement render, the current implementation we only care about adding to the DOM, after slowly expand the update and delete, please read according to the steps below, feel the process of thinking

//1. Create a DOM node based on the React element and add it to the root node to be mounted
function render(element, container) {
  const dom = document.createElement(element.type)
  container.appendChild(dom)
}
//2. Now we need to recursively do the same thing for the children of element
function render(element, container) {
  const dom = document.createElement(element.type)
  element.props.children.forEach((child) = > render(child, dom)) //+
  container.appendChild(dom)
}
//3. Remember that we have a special text wrapping node to deal with
function render(element, container) {
  const dom =
    element.type === "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type) //+-
  element.props.children.forEach((child) = > render(child, dom))
  container.appendChild(dom)
}
//4. Assign the props of element to the corresponding node
function render(element, container) {
  const dom =
    element.type === "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type)
  const isProperty = (key) = >key ! = ="children" //+ functions to filter the children of props
  Object.key(element.props)
    .filter(isProperty)
    .forEach((name) = > {
      dom[name] = element.props[name]
    }) //+
  element.props.children.forEach((child) = > render(child, dom))
  container.appendChild(dom)
}
Copy the code

Finally we’re done with the render function, and now you can combine it with createElement

    // The following is the pseudocode meaning can be
    function createElement(type, props, ... children) {
        //....
    }
    function createTextElement(text) {
        //....
    }
    function render(element, container){
        //....
    }
    const Didact = { createElement,render }
    const element = Didact.createElement(
      "div",
      { id: "foo" },
      Didact.createElement("a".null."bar"),
      Didact.createElement("b"))const container = document.getElementById("root")
    Didact.render(element, container)
Copy the code

That’s all for this node, the next node introduces React concurrent modes and Fibers, and optimizes the code that was implemented before, adding and modifying it to address its shortcomings