[JS] diff 알고리즘을 구현하면서 발생한 문제점

jiseong·2022년 5월 26일
0

T I Learned

목록 보기
255/291
post-custom-banner

발생했던 문제점

부모(App)와 자식(Todo) 컴포넌트가 있는 예시로 input에 대한 상태값은 자식이 관리하고 todo목록에 대해서는 부모가 관리하고 있다. 그래서 구현한 방식에 의해 사용자가 input 태그에 값을 입력하고 책추가 버튼을 누르면 부모 컴포넌트가 다시 렌더링이 된다.

그런데 여기서 부모 컴포넌트가 렌더링이 되는 시점에 자식 컴포넌트의 상태 값 즉, input값이 초기화되는 현상이 있었다. 원래의 목적이라면 기존의 컴포넌트 상태값을 가지고 있어 사용자가 새로운값을 입력하지 않아도 책추가를 클릭하면 입력했던 값이 포함되어야하지만 아무런 입력이 없는 값으로 보여지게 되었다.

원인

이벤트 핸들러에 의해 호출된 콜백함수의 this가 기존의 Todo가 아닌 새로운 Todo를 가리키는 것이 원인이였다.

해결방법

this가 기존의 컴포넌트를 가리키는 방식으로 수정하면 되는데 기존에는 다음과 같이 코드를 작성했었다.

기존

기존에는 비교알고리즘 내부에서 컴포넌트타입의 경우 기존의 vDOM객체와 비교하기 위해 인스턴스를 새로 생성하여 새로운 컴포넌트의 메서드를 호출하여 새로운 vDOM객체를 가져왔다.

const C =  vDOM.type;
const component = new C(vDOM.attributes || {});
const nextComponentVDOM = component.render();

수정

변경한 방식으로는 기존 컴포넌트의 내부 상태값을 업데이트 후에 기존 컴포넌트의 메서드를 호출하여 새로운 vDOM객체를 가져오는 방식이다.

const oldComponent = oldVDOM.DJ_COMPONENT;
oldComponent.updateProps(vDOM.attributes);
const next2ComponentVDOM = oldComponent.render();

사실 기존의 방식대로 구현했던 이유는 vDOM객체는 단지 비교를 위한 이라고 생각했기 때문인데 실제로도 확인해보면 반환되는 새로운 vDOM객체의 내부 내용은 완전 동일하다고 할 수 있다.

// new
const oldComponent = oldVDOM.DJ_COMPONENT;
oldComponent.updateProps(vDOM.attributes);
const next2ComponentVDOM = oldComponent.render();


// old
if(typeof vDOM.type === 'string') return;
const C =  vDOM.type;
const component = new C(vDOM.attributes || {});
const nextComponentVDOM = component.render();

그런데 조금 더 자세히 까보면 바인딩된 this값은 다르다는 것을 알 수 있다.

[[BoundThis]] 슬롯은 http://es5.github.io 사이트에서 다음과 같이 정의되어 있다.

The pre-bound this value of a function Object created using the standard built-in Function.prototype.bind method. Only ECMAScript objects created using Function.prototype.bind have a [[BoundThis]] internal property.

그리고 이후에 노드를 업데이트 하는 과정에서 해당 노드에 이벤트를 달아주기 때문에 호출되는 컴포넌트는 기존의 컴포넌트가 될 수 있게 된 것이다.

function updateNode(node: Element, vDOM: IDom, oldDOM?: IDom) {
  // 생략...
  node.addEventListener(eventType, value);
  // 생략...
}

Reference

post-custom-banner

0개의 댓글