React (step 2 ✌️)

Implement the mount of custom components

If you’ve used React, you’ll notice that we use a lot of custom components in React. We don’t currently support rendering custom tags in The Pauper version of React

// main.js
class MyComponent {
  constructor() {

  }
}  const customComponent = <MyComponent />  // Be sure to use the customComponent or webPack will declare only unused variables when packaging console.log(customComponent); Copy the code

Not surprisingly, we can see that our MyComponent type is created with createElement and passes a function object, which is definitely not supported by the native API.

So how do we do that? “Think about it. When we did React, did all custom classes inherit from react.component?”

So let’s go with the idea and write a base Component class that is recognized by createElement, and then all our custom Components inherit from it.

Our next task is to “encapsulate a Component class that can be rendered to a native page.”

  1. Props: As a rule of thumb, each Component can accept an argument to props
  2. Children: Can accept child components as children
  3. AppendChild: Adds children to the root node
  4. Root: root node (real DOM)
  5. There is a Render method to create itVirtual treeThe render method essentially calls the React.createElement method.plugin-transform-react-jsxHelped us do the work)

So!! We’ll also need to change our createElement method to return a real DOM. This doesn’t match the React design principle. We’ll need to make createElement return a virtual DOM

Methods: CreateElement and Document. createTextNode are called from the native Web API, stored in root variables. CreateElement returns only the instance of the enclosing class. To get the real DOM, we need to call root.

Create a new my-react.js file and put the logic of react.componentin a separate file

my-react
└ ─ ─ ─ dist│ │ main. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ the SRC│ │ main. Js│ │ my - react. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ webpack. Config. JsCopy the code

my-react.js

// my-react.js
export let React = {
  createElement: (tagName, attributes, ... children) = > {
    let ele;
    
 if (typeof tagName === 'string') {  // How to create a native tag  ele = new ElementWrapper(tagName, attributes);  } else {  ele = new tagName(attributes);  }  // Since our children are createElement logic, it is also a virtual DOM. Change child to vChild  children.forEach(vchild= > {  if (vchild === null) {  return;  }  if (typeof vchild === "string") {  vchild = new TextWrapper(vchild);  }  ele.appendChild(vchild);  })   return ele;  }, };  class ElementWrapper {  constructor(type, attributes) {  this.root = document.createElement(type);   Object.keys(attributes || {}).forEach(key= > {  if (key.match(/^on/)) {  let eventType = key.replace(/^on/.' ').toLocaleLowerCase();  this.root.addEventListener(eventType, attributes[key]);  return  }  this.root.setAttribute(key, attributes[key]);  });  }   appendChild(vchild) {  // appendChild essentially operates on the real DOM, so take the real DOM of the instance returned by createElment  this.root.appendChild(vchild.root);  } }  class TextWrapper {  constructor(content) {  this.root = document.createTextNode(content);  } }  class Component {  constructor(props) {  this.props = props;  this.children = [];   }   // Since we need to get the real DOM when rendering, we need a property to get root  get root() {  // Get the rendered virtual DOM and then the real DOM  return this.render().root;  }  }  React.Component = Component; Copy the code

Test our React in main.js

// main.js
import { React } from "./my-react";

class MyComponent extends React.Component {
  constructor() {}
  render() {  return (  <div id="id" style="background: red">  <span  onClick={()= > { console.log("add event success!" ); }}  >  zaoren1  </span>  <span>zaoren2</span>  </div>  );  } }  const customComponent = <MyComponent />;  document.body.appendChild(customComponent.root); Copy the code

Now that we’re ready to load our custom components, we’ll create a new react-dom.js file to write the render method in order to use it more closely to our React

my-react
└ ─ ─ ─ dist│ │ main. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ the SRC│ │ main. Js│ │ my - react. Js│ │ react - dom. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ webpack. Config. JsCopy the code

react-dom.js

// react-dom.js
export const render = function(vElement, parentDOM) {
  parentDOM.appendChild(vElement.root);
}
Copy the code

main.js

// main.js
+ import { render } from "./react-dom";
.+ render(<MyComponent />, document.body);
- document.body.appendChild(customComponent.root);
Copy the code

Implement setState to update the component state

// my-react.js
class Component {
  constructor(props) {
    this.props = props;
    this.children = [];
 this._root = null;  }   setState(state) {  this.state = state;   let oldRoot = this._root;   if (oldRoot && oldRoot.parentNode) {  oldRoot.parentNode.replaceChild(this.root, oldRoot);  }  }   // Since we need to get the real DOM when rendering, we need a property to get root  get root() {  // Get the rendered virtual DOM and then the real DOM  // Save a copy of root in _root every time you get root.  return this._root = this.render().root;  } } Copy the code
// main.js
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
 name: 'zaoren1'  }  }   render() {  const { name } = this.state;  return (  <div id="id" style="background: red">  <span  onClick={()= > {  this.setState({ name: 'setState success! ' })  }}  >  {name}  </span>  <span>zaoren2</span>  </div>  );  } } Copy the code

Then click on our zaoren1 and the content becomes setState Success!

Also, we can see that setState in React simply calls replaceChild to replace a child node under a parent node without re-rendering the entire DOM.

Project source code

This article is formatted using MDNICE