오늘은 라이프사이클 Hook과 템플릿 섹션의 라우트 변화에 반응할 수 있는 렌더리스 Vue 컴포넌트를 어떻게 만드는지 살펴봅시다.
결과가 너무 궁금하시다면 CodeSandBox에서 우리가 만들 결과물을 바로 확인하실 수 있습니다.
우리가 어떻게 라이프 사이클 훅을 다루기 위한 심플한 렌더리스 컴포넌트를 만들 수 있는지 살펴봅시다.
// 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 컴포넌트들을 재사용할 수 있습니다.
렌더리스 컴포넌트 패턴을 이용함으로써 컴포넌트의 템플릿에 발생하는 이벤트 hook을 제공하는 wrapper 컴포넌트를 만들면, 우리는 더 투명하고 쉽게 이해할 수 있고 높은 재사용성의 컴포넌트를 만들 수 있습니다.
하지만 신경써야 할 것이 몇 가지 더 있습니다. 꽤 많은 렌더리스 컴포넌트를 사용중이라면 여러 렌더리스 컴포넌트를 중첩시켜야 하는 문제에 직면할 수도 있습니다. 이 경우는 어떤 상황에서는 문제가 될 수 있어요. 그러므로 하나의 컴포넌트를 만들기 위해 필요한 렌더리스 컴포넌트의 수를 최소한으로 유지하기 위해 컴포넌트를 아주 단순하게 컴포넌트를 만드는 것을 목표로 해야 합니다.