React source code analysis 18 (9) ------ Implementing multi-node rendering [modify beginWork and completeWork]

Summary

Currently, we have implemented the single-node, beginWork, completeWork, and diff processes. But for multi-node situations, such as:

<div>
	<span></span>
	<span></span>
</div>

We haven't dealt with this situation yet, and this JSX will be converted to:

jsxs("div", {
    
    
  children: [jsx("span", {
    
    }),jsx("span", {
    
    })]
});

The children before are directly an object jsx because it is a single node. Now, it is represented by an array.
This article mainly deals with multi-node situations, so we need to modify our index.js file:

function App() {
    
    
  const [text, setText] = useState('100')
  const click1 = () => {
    
    
    setText(text + 1)
  }
  return jsx("div", {
    
    
    children: [jsx("div", {
    
    
      children: text,
      onClick: click1
    }), jsx("div", {
    
    
      children: ["text1","text2",jsx("span", {
    
    
        children: "span"
      })]
    })]
  })
}

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

1. Modify the beginWork process

Looking back again, the beginWork process mainly uses ReactElement to create Filber trees. Previously, we only considered the situation of return and child, and did not take sibling into consideration.

Now we need to add the sibling attribute to make the entire Filber tree more comprehensive, so we need to modify our reconcileChidren method.

Previously, in this method, when we judged the element type, there were FunctionComponent, HostComponent and HostText. Now because there are multiple nodes, element may also be an array.

If it is an array, we will continue to give the first node to parent.child. The remaining nodes are connected using sibling.
All sibling returns here still point to parent.

function reconcileChildren(parent,element) {
    
    
  //其他代码。。。
  }else if(Array.isArray(element) && element.length > 0) {
    
    
    let child = reconcileChildren(parent, element.shift());
    let head = child;
    while(element.length > 0) {
    
    
      let sibling = reconcileChildren(parent, element.shift());
      sibling.return = parent
      child.sibling = sibling;
      child = child.sibling;
    }
    return head;
  }
  //其他代码。。。
}

Then in updateHostComponent, the recursion of beginWork cannot only recurse child. Sibling also needs to be recursive:

function updateHostComponent(filberNode) {
    
    
  const nextChildren = filberNode.pendingProps.children;
  const newFilberNode = reconcileChildren(filberNode,nextChildren);
  filberNode.child = newFilberNode;
  newFilberNode.return = filberNode;
  beginWork(newFilberNode);
  if(newFilberNode.sibling) {
    
    
    beginWork(newFilberNode.sibling)
  }
}

Another problem is that for HostText type nodes, because it is impossible to have children, they were not processed in the previous recursive process.
But with sibling, for the HostText type, its sibling must also be recursed.

function updateHostText(filberNode) {
    
    
  if(filberNode.sibling) {
    
    
    beginWork(filberNode.sibling)
  }
}

export const beginWork = (nowFilberNode) => {
    
    
  switch (nowFilberNode.tag) {
    
    
	//其他代码。。。
    case HostText: {
    
    
      return updateHostText(nowFilberNode)
    }
    case FunctionComponent: {
    
    

    }
    default: {
    
    
      console.error('错误的类型')
    }
  }
}

2. Process the completeWork process

In completeWork, we mainly build the off-screen DOM tree and then mount it on the stateNode. For this process, we only need to consider the sibling attribute during the recursive process:

For HostComponent type:

function completeHostComponent(filberNode) {
    
    
  const type = filberNode.type;
  const element = document.createElement(type);
  addPropsToDOM(element, filberNode.pendingProps)
  filberNode.stateNode = element;
  const parent = filberNode.return;
  if(parent && parent.stateNode && parent.tag === HostComponent) {
    
    
    parent.stateNode.appendChild(element)
  }
  completeWork(filberNode.child);
  if(filberNode.sibling) {
    
    
    completeWork(filberNode.sibling)
  }
}

For HostText type:

function completeHostText(filberNode) {
    
    
  const content = filberNode.pendingProps;
  const element = document.createTextNode(content)
  filberNode.stateNode = element
  const parent = filberNode.return;
  if(parent && parent.stateNode && parent.tag === HostComponent) {
    
    
    parent.stateNode.appendChild(element)
  }
  if(filberNode.sibling) {
    
    
    completeWork(filberNode.sibling)
  }
}

In this way, we have finished processing the multi-node mount rendering.

Guess you like

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