[ProPro] Selector 컴포넌트 분리

jiseong·2022년 5월 2일
0

T I Learned

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

ProPro의 프로필 페이지에는 지역, 직무등 여러 개의 select tag가 사용되고 있다.

현재는 하나의 Form 컴포넌트에서 각 select tag의 옵션들을 동적으로 생성하고 이벤트를 달아주는 일을 하고 있는데 select tag의 옵션들을 생성하는 단계에서 중복적으로 보이는 코드가 존재하고 Form 컴포넌트에서 너무 많은 일을 하고 있는 것 같아서 Selector라는 컴포넌트를 생성하여 분리하고자 한다.

renderCallback() {
    const sidoSelect = this.container.querySelector('.sido-select');
    const positionSelect = this.container.querySelector('.position-select');

    this.$sidos = new DocumentFragment();
    defaultSido.forEach(sido => {
      const opt = createDom('option', {
        value: sido,
        innerHTML: sido,
      });
      if (sido === this.props.userInfo.region.sido) opt.selected = true;
      this.$sidos.appendChild(opt);
    });

    this.$positions = new DocumentFragment();
    defaultPosition.forEach(position => {
      const opt = document.createElement('option');
      [opt.value, opt.innerHTML] = position;
      if (this.props.userInfo.position === position[0]) opt.selected = true;
      this.$positions.appendChild(opt);
    });

    sidoSelect.appendChild(this.$sidos);
    positionSelect.appendChild(this.$positions);
}


setEvent() {
    const sidoSelect = this.container.querySelector('.sido-select');
    const positionSelect = this.container.querySelector('.position-select');

    sidoSelect.addEventListener('change', event => {
      this.props.onChangeUserInfo({
        ...this.props.userInfo,
        region: {
          sido: event.target.value,
          sigungu: '',
        },
      });

      this.sigunguChange(event.target.value);
    });

    positionSelect.addEventListener('change', event => {
      this.props.onChangeUserInfo({
        ...this.props.userInfo,
        position: event.target.value,
      });
    });
}

우선, 모든 select tag는 옵션 목록들을 받아 체크유무만 확인하여 옵션을 생성하는 과정으로 진행되기 때문에 이런 중복적인 부분들을 제거하기 위해 생성해야할 옵션들을 props로 받아 옵션들을 생성하는 컴포넌트를 만들었다.

class Selector extends CustomComponent {
  markup() {
    const { items, defaultOption, selectedItem } = this.props;
    return `
      <option value="" selected disabled hidden>${defaultOption}</option>
      ${items
        .map(
          item => `
          <option value="${item}" 
          ${item === selectedItem ? 'selected' : ''}>${item}</option>
          `,
        )
        .join('')}
    `;
  }

  setEvent() {
    const { onChange } = this.props;
    this.container.onchange = onChange;
  }
   // 생략...
}

그리고 해당 컴포넌트에서 select tag의 옵션들이 선택될 때마다 호출되기 위한 이벤트 핸들러를 달아준다.

이벤트 핸들러 등록 방식으로 addEventListener()를 사용하지 않은 이유는 시/군에 따라 시/군/구 select tag가 바뀌는데 바뀔때마다 select 컴포넌트가 생성되면서 이벤트를 달아주기 때문에 여러 개의 이벤트 핸들러가 달리게 되는 상황이 발생하여 하나의 이벤트만 달 수 있는 프로터피 방식을 사용하게 되었다.

class Selector extends CustomComponent {
  // 생략...
  setEvent() {
    const { onChange } = this.props;
    this.container.onchange = onChange;
  }
}

만들고보니 마크업과 이벤트 핸들러 등록을 하위 컴포넌트에서 작업하는 것 말고는 바뀐게 없는 것 같은데 그래도 이전에 비해서 중복되는 코드가 통일되고 필요한 정보들이 한 곳에 몰려있다보니 코드가 깔끔해지고 흐름을 이해하기 쉬워지게 된 것 같다.

renderCallback() {
    new Selector({
      container: sidoSelect,
      props: {
        defaultOption: '시/도',
        items: defaultSido,
        selectedItem: this.props.userInfo.current.region.sido,
        onChange: event => this.handleSidoChange(event),
      },
    });

    new Selector({
      container: positionSelect,
      props: {
        defaultOption: '직무를 선택하세요',
        items: defaultPosition,
        selectedItem: this.props.userInfo.current.position,
        onChange: event => this.handlePostionChange(event),
      },
    });
}
post-custom-banner

0개의 댓글