React source code analysis 18 (5) ------ Implementing function components [Modify beginWork and completeWork]

Summary

After several previous articles, we have implemented basic jsx in the page rendering process. However, if the component is written through a function component, it still cannot be rendered on the page.
So this article is mainly about modifying the previously written method to be able to display function components, so now we modify the way jsx is written in the index.js file. Modify it into a function component:

import jsx from '../src/react/jsx.js'
import ReactDOM from '../src/react-dom/index'

const root = document.querySelector('#root');

function App() {
    
    
  return jsx("div", {
    
    
    ref: "123",
    children: jsx("span", {
    
    
      children: "456"
    })
  });
}

ReactDOM.createRoot(root).render(<App />)

Here we need to use our own jsx method. Therefore, what is returned in the App is still called through the previous method.

1. Modify reconcileChildren method

Let's recall that in the beginWork stage, we mainly create FilberNode through ReactElement. And reconcileChildren is the method to create FilberNode.

Previously, we only dealt with the HostText type and the HostComponent type, so in this method, we need to make the function type compatible. As a function component, the most obvious feature of ReactElment is that the value of type is a function.

For example, for the App component above, the corresponding ReactElement type is App. So we can determine the type of component by type:

function reconcileChildren(element) {
    
    
  let tag;
  if(typeof element.type === 'function') {
    
    
    tag = FunctionComponent
  }
  //其他代码
  console.log(filberNode)
  return filberNode
}

Let’s print it to see if this function component meets expectations:

Insert image description here

2.updateFunctionComponent method

Now that we have a FilberNode with the tag FunctionComponent type, in beginWork, we have to process this type of FilberNode:

function beginWork(nowFilberNode) {
    
    
  switch (nowFilberNode.tag) {
    
    
  	//其他代码
    case FunctionComponent: {
    
    
      return updateFunctionComponent(nowFilberNode)
    }
    //其他代码
  }
}

Now we implement the updateFunctionComponent method.
Previously, for the FilberNode of the HostComponent type, its child node was actually its corresponding ReactElement.

But for the function type FilberNode, let's think about it, isn't it its own return value? So we can get its child FilberNode by calling this function directly.

function updateFunctionComponent(filberNode) {
    
    
  const nextChildren = filberNode.type();
  const newFilberNode = reconcileChildren(nextChildren);
  filberNode.child = newFilberNode;
  newFilberNode.return = filberNode;
  beginWork(newFilberNode)
}

2. Modify the completeWork method

For the completeWork method, its main function (currently) is to add stateNode to the corresponding FilberNode, and the function component does not have its own corresponding StateNode, so just continue the recursion:

export const completeWork = (filberNode) => {
    
    
  const tag = filberNode.tag
  switch (tag) {
    
    
	//其他代码。。。
    case FunctionComponent: {
    
    
      completeWork(filberNode.child)
    }
  }
}

3. Modify the commitWork method

For the previous commitWork, we directly mounted the stateNode of the outermost FilberNode on the container. Now, since the outermost layer may be FunctionComponent, it does not have its own stateNode. So we need to find the outermost FilberNode with stateNode.

import {
    
     HostComponent } from "./filberNode";

export function commitWork(filberRootNode) {
    
    
  const container = filberRootNode.container;
  let node = filberRootNode.finishedWork;
  while( node.tag !== HostComponent ){
    
    
    node = node.child
  }
  container.appendChild(node.stateNode)
}

OK, after the above modifications, our App component can also render normally.

Guess you like

Origin blog.csdn.net/weixin_46726346/article/details/132269209