This is the 18th day of my participation in the Gwen Challenge.More article challenges

Today we are going to look at class component updates:

  • Implement setState to guard against updates to implementation class components
  • The component updates to a situation where it is not the same component
  • The case where the updated component and the old component are the same component

Implement class component updates using the setState method

1) Declare a class component with state

  1. First of all insrc/index.jsSet up a class componentAlert, set class component state there is oneTitleProperty that is executed when the button is clickedhandleClickFunction will be calledsetState({title: "changed Title"})The title method changes the title property.

How do we implement setState that changes the value of state and updates the component DOM?

class Alert extends TinyReact.Component {
  constructor(props) {
    super(props)
    this.state = {
      title: "Default Title"
    }
    this.handleClick = this.handleClick.bind(this)}handleClick() {
    this.setState({title: "changed Title"})}render() {
    console.log(this.state)
    return (
      <div>
        {this.props.name}
        {this.props.age}
        <div>
          {this.state.title}
          <button onClick={this.handleClick}>Change the Title</button>
        </div>
      </div>
    )
  }
}
TinyReact.render(<Alert name="Zhang" age={20} />, root);
Copy the code

2) Implement setState:

  • Since we use the setState method to update components, we should continue to write the Component class to fulfill the need for setState to update the DOM. So add a setState method to the Component class to update the state value.
setState() {
    // Merge the value of the new state into the state to complete the state update operation
     this.state = Object.assign({}, this.state, state)
}
Copy the code
  • Next, complete the DOM update with the change of state. To complete the DOM update, you must find the VirtualDOM generated when updating the DOM and the oldDOM rendered, and compare the two DOM to complete the update. When state changes, we can use the Render method of the Alert component to get the new VirtualDOM.
setState() {
 // Merge the value of the new state into the state to complete the state update operation
  this.state = Object.assign({}, this.state, state)
 // Get the latest VirtualDOM object to render
 let virtualDOM = this.render()
}
Copy the code
  • How do I get the oldDOM of the class component? The old DOM object was added to the page via container. AppendChild (newElement). That is, the newElement is the oldDOM displayed on the page. We know this is oldDOM, but how do we pass this oldDOM into the class component instance object?

  • In the Component class, we define a method called setDOM and pass the dom argument to store the oldDOM object in the instance object of the Component. That is, we call the Component’s setDOM method from the mountNativeElement function. Pass oldDOM into the class component instance object. The oldDOM instance object is then returned via the getDOM method.

  • Another question is how do we in mountNativeElement how do we call the setDOM method, to call the setDOM method, we have to get the class component instance object, and then we can get the setDOM method through the class component instance object. When we say Component instance object, we are not referring to the Component generated instance, because in our code implementation we do not instantiate Component with new Component(). Instead, we instantiate its subclasses. Subclasses of Component can also get setDOM methods. In mountComponent.js there is a buildClassComponent function that gets the component instance object. This method returns a virtualDOM, The returned virtualDOM is nextVirtualDOM, which happens to be passed to the first parameter of the mountNativeElement, We can mount the Component instance object conponent to nextVirtualDOM and upload it to the mountNativeElement method, where we can call the setDOM method to set the oldDOM of the page to the Component.

  • Code implementation

//mountComponent.js
function buildClassComponent(virtualDOM) {
 const component = new virtualDOM.type(virtualDOM.props || {});
 const nextVirtualDOM = component.render()
 nextVirtualDOM.component = component // Mount the component instance object to the class component nextVirtualDOM and pass it into mountNativeElement
 return nextVirtualDOM
}

// mountNativeElement.js

export default function mountNativeElement(virtualDOM, container, oldDOM) {
 letnewElement = createDOMElement(virtualDOM); ...// Get the class component instance
 let component = virtualDOM.component
  // Get the class component instance, or call its setDOM to set the oldDOM of the corresponding page to Conponent
  // Why do we need to determine, because there is also a function component, function component is not setDOM
 if(component) { component.setDOM(newElement); }}// Component.js
export default class Component {
 constructor (props) {
   this.props = props
 }
 setState(state) {
   this.state = Object.assign({}, this.state, state)
   // Get the latest VirtualDOM object to render
   let virtualDOM = this.render()
   // Get the old virtualDOM object for comparison
   let oldDOM = this.getDOM()
   console.log(oldDOM)
 }
 setDOM(dom) {
   // Set oldDOM (VirtualDOM object for the page)
   this._dom = dom
 }
getDOM() {
   return this._dom
 }
Copy the code
  • Obtain the new virtualDOM after oldDOM and the current state update, and compare them to complete the DOM update operation. Obviously, calldiffThe contanier component container (component mount point) can be obtained from oldDOM’s parentNode.
setState(state) {
    this.state = Object.assign({}, this.state, state);
    // Get the latest VirtualDOM object to render
    let virtualDOM = this.render();
    // Get the old virtualDOM object for comparison
    let oldDOM = this.getDOM();
    // Get the container
    let container = oldDOM.parentNode;
    // Implement an update to the object component
    diff(virtualDOM, container, oldDOM);
  }
Copy the code

Click on the button

2. Update components

To complete component updates, you should determine in the diff method that the VirtualDOM to be updated is a component.

If the component determines whether the component to be updated is the same as the unupdated component, it does not need to update the component. Instead, use the mountElement method to add the VirtualDOM returned by the component to the page

If it is the same component, update the component, passing the latest props to the component, calling the render method of the component to get the latest VirtualDOM object returned by the component, passing the VirtualDOM object to the diff method to find the difference. This updates the difference into the real DOM object.

Different component lifecycle functions are called at different stages of updating a component.

The next step is to implement the above ideas:

  • Check whether the VirtualDOM to be updated is a component in the Diff method. If it is a component, add the diffComponent method to handle it and update the component in this method. Declare a method in the DiffComponent.js fileisSameComponentDetermine if it is the same component and deal with it in two ways.

The diffComponent takes four arguments:

  • VirtualDOM: A virtual object of the component itself, from which you can obtain the latest props of the component
  • OldComponent: An instance object of the component to be updated. It replaces the lifecycle functions of the component, updates the props property of the component, and returns the latest VirtualDOM with the group keys
  • OldDOM is the DOM object to update. When updating components, you need to modify the existing DOM object to minimize DOM operations and obtain the old VirtualDOM object
  • Container If the component to be updated is not the same as the old component, you need to use Container as the parent container to display the VirtualDOM returned by the component directly on the page
//diff.js
else if (typeof virtualDOM.type === "function") {
    / / component
    diffComponent(virtualDOM, oldComponent, oldDOM, container)
}
Copy the code
export default function diffComponent (virtualDOM, oldComponent, oldDOM, container) {
  console.log("boolean", isSameComponent(virtualDOM, oldComponent));
  // Check if it is the same component
  if(isSameComponent(virtualDOM, oldComponent)) {
    // Update the same component
    console.log('Same component')}else {
    // Not the same component
    console.log("Not the same component"); }}function isSameComponent (virtualDOM, oldComponent) {
  return oldComponent && virtualDOM.type === oldComponent.constructor
}
Copy the code

Not the same component

  • If the component to be updated is not the same as the old component, and the VirtualDOM returned by the component is displayed directly on the page, we call the mountELement method to complete the rendering of the VirtualDOM component using the current VirtualDOM and container parameters
if(isSameComponent(virtualDOM, oldComponent)) {
   // Update the same component
   console.log('Same component')}else {
   // Not the same component
   console.log("Not the same component");
   mountElement(virtualDOM, container);
 }

Copy the code

One problem with this is that oldDOM is still on the page even though the new virtualDOM has been rendered, so we need to extend the mountELement function to pass oldDOM in and remove it. Components are updated.

// diffComponent.js
else {
   // Not the same component
   console.log("Not the same component");
   mountElement(virtualDOM, container, oldName);
 }
// mountElement.js
export default function mountElement(virtualDOM, container, oldDOM) {
 // Component VS NativeElement
 if(isFunction(virtualDOM)) {
   // Component
   mountComponent(virtualDOM, container, oldDOM); // Pass old to be removed when mountComponent calls mountNativeElement
 } else {
   // NativeElement
   mountNativeElement(virtualDOM, container, oldDOM);// Pass old to mountNativeElement to delete}}export default function mountNativeElement(virtualDOM, container, oldDOM) {
 let newElement = createDOMElement(virtualDOM);

 // Determine whether the old DOM object exists, if so, delete it
 if(oldDOM) {
   unmountNode(oldDOM)
 }

Copy the code

Same component

  • The method for updating the same component is namedupdateComponent, at the same time need four parameters virtualDOM oldComponent, oldDOM, container. Declare a method in the Component class updateProps to update the props of the Component instance. The component instance oldComponent is used to call updateProps to update the props of the class component. After the update, the component instance is placed in the nextVirtualDOM to be rendered, and the diff method is executed to update the component.
export default function updateComponent(virtualDOM, oldComponent, oldDOM, container) {
  // Component update
  oldComponent.updateProps(virtualDOM.props)
  let nextVirtualDOM = oldComponent.render()
  nextVirtualDOM.component = oldComponent
  diff(nextVirtualDOM, container, oldDOM)
  / / update the props
}

Copy the code
  • There are a few things we need to do, and we need to use some lifecycle functions when the component is updated. We put these periodic functions in the Component class and override them when the child components use them. In updateComponent, the child’s lifecycle functions are called first, and the Component’s lifecycle methods are called if the child hasn’t been overridden. UpdateComponent calls the corresponding periodic function using the component instance object oldComponent
  • The most began to perform componentWillReceiveProps periodic function
  • ShouldComponentUpdate is then executed to see if the component properties are updated, and the update is executed down
  • Pass the props to componentWillUpdate periodic function
  • Finally, the componentDidUpdate method is called after the alignment
// Component.js
export default class Component {
  constructor(props) {
    this.props = props; }...updateProps(props) {
    this.props = props;
  }
  // Lifecycle functions
  componentWillMount() {}
  componentDidMount() {}
  componentWillReceiveProps(nextProps) {}
  shouldComponentUpdate(nextProps, nextState) {
    returnnextProps ! =this.props || nextState ! =this.state;
  }
  componentWillUpdate(nextProps, nextState) {}
  componentDidUpdate(prevProps, preState) {}
  componentWillUnmount(){}}// updateComponent.js
import diff from "./diff"

export default function updateComponent(virtualDOM, oldComponent, oldDOM, container) {
  oldComponent.componentWillReceiveProps(virtualDOM.props);
  if (oldComponent.shouldComponentUpdate(virtualDOM.props)) {
    // Props before
    let prevProps = oldComponent.props;
    oldComponent.componentWillUpdate(virtualDOM.props);
    // Component update
    oldComponent.updateProps(virtualDOM.props);
    // Get the latest virtualDOM returned by the component
    let nextVirtualDOM = oldComponent.render();
    // Update the Component instance object
    nextVirtualDOM.component = oldComponent;
    / / thandiff(nextVirtualDOM, container, oldDOM); oldComponent.componentDidUpdate(prevProps); }}Copy the code

Today, we mainly implement an implementation of class component update. At first, we use setState to change the component state to change the component update. First, we implement the new state value and the component old state to merge, and then use the updated value to obtain the latest virtualDOM, and then through setDOM and getDOM methods, Call setDOM in mountNativeElement to store oldDOM object to Component, oldDOM through getDOM channel, and diff to update Component. VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) : VirtualDOM (VirtualDOM) However, oldDOM needs to be deleted. Therefore, pass oldDOM to mountELement to delete oldDOM. In the same Component case, just replace the Component properties, so declare an updateProps in the Component class, In the updateComponent function, oldComponnet Component instance is used to call updateProps to update properties. Diff is used to perform DOM updates. Finally, periodic functions are embedded in the updateComponent Component, which are declared in the Component and can be called by subcomponents. Set up the sequence in updateComponent to complete the periodic function hook.

That’s all for today, day 7 we finish the Virtual DOM and Diff algorithm:

  • Implement the ref attribute to get the element DOM object to get the component instance image
  • Use the Key attribute to compare nodes
  • Remove nodes

Thanks for liking the discussion and stay tuned to…..