render phase와 commit phase를 분리시켜보자.

Untitled

updateVirtualDOM 함수를 저번 주차에 만들었었다. 하지만 이 코드는 함수 이름처럼 V-DOM만 update하지 않고, realDOM 까지 update해버린다.

내가 원하는 방식은 이거다.

updateVirtualDOMupdateRealDOM

realDOM에 영향을 주는 코드들은 다음과 같다.

Untitled

updateVirtualDOM 이 완료가 된 후에 updateRealDOM 함수가 실행되야 하므로 저 밑줄친 부분을 어떤 곳에 저장해두었다가 인자 값으로 넘겨주면 되겠다고 생각했다.

Map을 사용해서 이런 식으로 구현해보았다.

export function updateVirtualDOM(root, oldNode, newNode, commitMap, index = 0) {
  if (!oldNode) return commitMap.set('appendChild', { root, child: createVirtualDOM(newNode) });
  if (!newNode) return commitMap.set('removeChild', { root, child: createVirtualDOM(root.childNodes[index]) });
  if (oldNode.type !== newNode.type) return commitMap.set('replaceChild', { root, newChild: createVirtualDOM(newNode), oldChild: root.childNodes[index] });

  if (
    oldNode.type === 'TEXT_ELEMENT'
    && newNode.type === 'TEXT_ELEMENT'
    && oldNode.props.nodeValue !== newNode.props.nodeValue
  ) {
    const newElement = createVirtualDOM(newNode);
    return commitMap.set('replaceChild', { root, newChild: newElement, oldChild: root.childNodes[index] });
  }

  const oldProps = oldNode.props;
  const newProps = newNode.props;
  if (oldNode.type !== 'TEXT_ELEMENT' && oldNode.type !== 'FRAGMENT') {
    commitMap.set('updateElement', { element: root.childNodes[index], newProps, oldProps });
  }

  const max = Math.max(oldProps.children.length, newProps.children.length);

  for (let i = 0; i < max; i++) {
    updateVirtualDOM(
      oldNode.type === 'FRAGMENT' ? root : root.childNodes[index],
      oldProps.children[i],
      newProps.children[i],
      commitMap,
      i
    );
  }

  return root;
}

export function updateRealDOM(commitMap) {
  commitMap.forEach((value, key) => {
    switch (key) {
      case 'appendChild':
        value.root.appendChild(value.child);
        break;
      case 'removeChild':
        value.root.removeChild(value.child);
        break;
      case 'replaceChild':
        value.root.replaceChild(value.newChild, value.oldChild);
        break;
      case 'updateElement':
        getElementWithStyle(value.element, value.newProps, value.oldProps);
        break;
    }
  });
}

하지만 이렇게 했을 때 에러가 띄워졌다.

getElementWithStyle가 나중에 적용되다보니 이벤트 핸들러가 이상하게 인식해서 + 버튼을 눌러도 2 이상 올라가지 않는 현상이 발생했다.