렌더리스 Vue.js 라이프사이클 Hook 컴포넌트

Kim Kyeseung·2021년 1월 11일
0
post-thumbnail

오늘은 라이프사이클 Hook과 템플릿 섹션의 라우트 변화에 반응할 수 있는 렌더리스 Vue 컴포넌트를 어떻게 만드는지 살펴봅시다.

결과가 너무 궁금하시다면 CodeSandBox에서 우리가 만들 결과물을 바로 확인하실 수 있습니다.

Lifecycle hooks

우리가 어떻게 라이프 사이클 훅을 다루기 위한 심플한 렌더리스 컴포넌트를 만들 수 있는지 살펴봅시다.

// src/components/FrameHooks.vue
export default {
  created() {
    // As soon as this component is created, it's parent component
    // must have already been created, so we immediately trigger the hook.
    this.$emit('created');

    const triggerMounted = () => this.$emit('mounted');
    this.$parent.$on('hook:mounted', triggerMounted);

    const triggerUpdated = () => this.$emit('updated');
    this.$parent.$on('hook:updated', triggerUpdated);

    this.$once('hook:beforeDestroy', () => {
      this.$parent.$off('hook:mounted', triggerMounted);
      this.$parent.$off('hook:updated', triggerUpdated);
    });
  },
  render() {
    // Render the first child of the default slot.
    return this.$slots.default[0];
  },
};

기사 목록을 받아오는 API 요청을 위해 새로운 컴포넌트 FrameHooks 를 렌더리스 컴포넌트 ArticleListFrame와 결합하여 어떻게 사용하는지 예시를 통해서 볼 수 있습니다.

<template>
  <ArticleListFrame
    v-slot="{ articles, methods: { fetchList } }"
  >
    <FrameHooks @created="fetchList">
      <ul class="ArticleList">
        <li v-for="article in articles">
          {{ article.title }}
        </li>
      </ul>
    </FrameHooks>
  </ArticleListFrame>
</template>

보시다시피 FrameHooks 컴포넌트는 특히 다른 렌더리스 컴포넌트로 받은 메소드들을 다루기에 유용합니다. 이것은 FrameHooks 를 활용하는 수 많은 방법들중 하나일 뿐입니다.

라우트 체인지

특정 변화에 반응하는 훅을 사용하는 또 다른 영역은 라우팅입니다.

// src/components/FrameHooks.vue
export default {
  watch: {
    $route: {
      handler(current, prev) {
        const currentQueryString = JSON.stringify(current.query);
        const prevQueryString = JSON.stringify(prev.query);
        const queryChanged = currentQueryString !== prevQueryString;
        if (queryChanged) {
          this.$emit('route-query-change', current.query);
        }
      },
    },
  },
  created() {
    // ...
  },
  // ...
};

다음으로 라우트의 쿼리 파라미터가 바뀔때마다 fetchList()를 호출하기 위해 route-query-change 훅을 사용하는 법을 볼 수 있습니다. 만약 예를 들어 라우트가 /articles?page=1 에서 /articles?page=2로 바뀐다면 fetchList()가 호출되어 2 페이지의 기사가 불러와질 겁니다.

<template>
  <ArticleListFrame
    v-slot="{
      articles,
      methods: { fetchList }
    }"
  >
    <FrameHooks
      @created="fetchList({ page: $route.query.page });"
      @route-query-change="fetchList({ page: $event.page });"
    >
      <div class="Home">
        <ul class="ArticleList">
          <li v-for="article in articles" :key="article.title">
            {{ article.title }}
          </li>
        </ul>
        <RouterLink
          :to="{ query: { page: 1 } }"
        >
          Page 1
        </RouterLink> |
        <RouterLink
          :to="{ query: { page: 2 } }"
        >
          Page 2
        </RouterLink>
      </div>
    </FrameHooks>
  </ArticleListFrame>
</template>

이 패턴이 얼마나 강력한지 단순한 예시로서 이미 증명이 되었다고 생각이 되는데요, 우리는 자바스크립트 코드 한 줄(함수 호출은 제외하고)도 손대지 않고도 페이지네이션 같은 기능을 통합하기 위해 우리의 어플리케이션에 있는 Frame 컴포넌트들을 재사용할 수 있습니다.

CodeSandBox

끝으로

렌더리스 컴포넌트 패턴을 이용함으로써 컴포넌트의 템플릿에 발생하는 이벤트 hook을 제공하는 wrapper 컴포넌트를 만들면, 우리는 더 투명하고 쉽게 이해할 수 있고 높은 재사용성의 컴포넌트를 만들 수 있습니다.

하지만 신경써야 할 것이 몇 가지 더 있습니다. 꽤 많은 렌더리스 컴포넌트를 사용중이라면 여러 렌더리스 컴포넌트를 중첩시켜야 하는 문제에 직면할 수도 있습니다. 이 경우는 어떤 상황에서는 문제가 될 수 있어요. 그러므로 하나의 컴포넌트를 만들기 위해 필요한 렌더리스 컴포넌트의 수를 최소한으로 유지하기 위해 컴포넌트를 아주 단순하게 컴포넌트를 만드는 것을 목표로 해야 합니다.

profile
웹 프론트엔드 개발자입니다.

0개의 댓글