[황준일]Vanilla Javascript로 웹 컴포넌트 만들기를 읽은 후기

jaemin·2023년 4월 13일
0

render VS mounted

컴포넌트를 분할하면서, 원래 render 메서드 하나로만 관리하다가 render 메서드mounted 메서드를 분리했다. render와 mounted를 분리한 이유가 뭘까?
render 안에서 mounted가 호출된다. 만약 특정 돔에 하위 컴포넌트를 추가해야 할 때 특정 돔이 만들어지지 않았을 수 있기 때문에

<div id="app"></div>

처럼 미리 존재해야할 돔을 미리 렌더링 되어야 한다. 이 과정을 render에서 하고 나중에 추가돼야할 컴포넌트는 mounted에서 렌더링 된다.

this

this binding이 필요한 이유

  • src/App.js
class App extends Component {
	mounted() {
      new Items($items, {
        /*
        filteredItems,
        toggleItem: toggleItem.bind(this),
        */![](https://velog.velcdn.com/images/kim-jaemin420/post/b680d4f5-6fd4-4d50-a70e-edc8d982c752/image.png)

        deleteItem: deleteItem.bind(this),
        
      })
	}
  
    deleteItem(seq) {
      const items = [...this.state.items];
      
      items.splice(items.findIndex(item => item.seq === seq), 1);
      this.setState({ items });
    }
}

export default App;

여기서 deleteItem, toggleItem 메서드에 this를 바인딩해주는 이유가 뭘까?

  • src/components/Items.js
class Items {
	setEvent() {
      const { deleteItem, toggleItem } = this.props;

      this.addEvent('click', '.deleteBtn', ({target}) => {
        deleteItem(Number(target.closest('[data-seq]').dataset.seq));
      });
    }
}

export default Items;

Items 컴포넌트에서 this.props.deleteItem으로 접근 가능하다. 그리고 deleteItems 안에서는 this.state.items를 참조하고 있다.
this는 함수를 호출하는 형태에 따라 바인딩이 달라진다.

호출 방식에 따른 this

  • 일반 함수처럼 호출: window
  • 메소드로 호출: 메서드를 호출한 객체
  • 생성자 함수 호출 : 생성자 함수가 (미래에) 생성할 인스턴스

처음 글을 읽을때, this binding을 왜 꼭 해줘야하는지 이해가 안돼서 binding 해주지 않고 this.props로 넘겨줘봤다.

이때, Items에서 호출하게 되면, deleteItem를 일반 함수 호출하듯이 호출했으므로 deleteItem 메서드 안에서 참조하는 this는 window 객체를 가리키고 있을거라고 예상했다.

  • src/App.js
class App {
	mounted() {
      new Items($items, {
        deleteItem,
        //...
      })
	}
  
    deleteItem() {
      // this???
    }
}

export defualt App
class Items {
	setEvent() {
      const { deleteItem, toggleItem } = this.props;

      this.addEvent('click', '.deleteBtn', ({target}) => {
        deleteItem(Number(target.closest('[data-seq]').dataset.seq));
      });
    }
}

그러나 실제로 window 객체가 찍히는게 아니라 undefined가 찍히는 것을 볼 수 있었다.

strict mode일 경우 일반 함수로 호출 시 this는 undefined가 바인딩 되는데, 따로 strict mode를 설정하지 않았는데 undefined가 되는게 이해가 가지 않았다.
알고 보니 module을 사용하면 자동으로 strict mode가 되기 때문에 this가 undefined로 잡히는 것을 알 수 있었다.

그렇다면, Items에서 일반 함수가 아닌 this.props.deleteItem()으로 호출했을때 deleteItem 메서드 안에서의 this는 어떤 것을 가리키게 될까?
이 경우에 this는 당연히 this.props를 가리키게 된다.

deleteItem 메서드안에서 필요한 this는 this.props객체가 아니라 App 생성자 함수가 만들어낸 인스턴스 객체이다. 이렇게 메서드를 어떻게 호출되냐에 따라 this가 변경되어 혼동을 준다. 또 다른 컴포넌트에서 deleteItem 메서드를 호출시켜 App의 상태를 변경시키므로 this binding 없이는 App의 상태를 변경할 방법이 없다. 따라서 꼭 binding을 해줘야 한다는 것을 알 수 있었다.

this binding을 하고 메서드를 내 맘대로 호출하면?

this binding을 해주어도 함수를 내 마음대로 호출하면 어떻게 될지 궁금해졌다.

  • src/App.js
class App {
	mounted() {
      new Items($items, {
        deleteItem: deleteItem.bind(this),
        //...
      })
	}
  
    deleteItem() {...}
}

export defualt App
  • src/components/Items.js
class Items {
	setEvent() {
      this.addEvent('click', '.deleteBtn', ({target}) => {
        this.props.deleteItem(Number(target.closest('[data-seq]').dataset.seq));
      });
    }
}

this.props.deleteItem()으로 호출했음에도 불구하고 bind 해준 this가 찍히는 걸 알 수 있다.

profile
프론트엔드 개발자가 되기 위해 공부 중입니다.

0개의 댓글