Post

[Vue] HOC | high order component | Component recycling

[Vue] HOC | high order component | Component recycling

HOC (high order component)

뷰의 하이 오더 컴포넌트는 리액트의 하이 오더 컴포넌트에서 기원된 것입니다. 리액트의 하이 오더 컴포넌트 소개 페이지를 보면 아래와 같이 정확한 정의가 나와 있습니다. A higher-order component (HOC) is an advanced technique iin React for reusing component logic. 이 말을 정리해보면 다음과 같습니다.

하이 오더 컴포넌트는 컴포넌트의 로직(코드)을 재사용하기 위한 고급 기술입니다.

반복되는 컴포넌트 로직

여기서 커포넌트 로직을 재사용한다는 말은 무슨 의미일까요? 이 표현에서 가리키고 있는 컴포넌트 로직이란 뷰에서 인스턴스 옵션을 의미합니다. 컴포넌트 들에서 공통적으로 들어가는 인스턴스 옵션이 있을 때 반복되는 코드를 줄여줄 수 있습니다.

1. high order component 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import EventBus from '../utils/EventBus.js'
import ListView from '../components/ListView.vue';

// high order component
export default function createListView(componentName){
  return {
    // 재사용 할 인스턴스(컴포넌트) 옵션들이 들어갈 자리
    name: componentName,
    mounted() {
      EventBus.$emit('off:loading');
      this.$store.dispatch('FETCH_LIST', this.$route.name)
        .then( () => EventBus.$emit('end:spinner')
        .catch( e => console.log(e) );
    },
    
    // render() 함수로 컴포넌트를 로딩한다.
    render(createComponent) {
      return createComponent(ListView);
    }
  }
}

위 코드는 CreateListComponent라는 하이 오더 컴포넌트를 구현한 코드입니다.
하이 오더 컴포넌트를 적용한 컴포넌트들의 공통 코드들(mounted, name 등)을 미리 정의합니다.
그리고 이 하이 오더 컴포넌트를 router 파일에서 사용합니다.

2. router에서 HOC 연결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import createListView from '../views/createListView.js';  // HOC

new VueRouter({
  routes: [
    {
      path: '/news',
      component: createListView('NewsView')
    },
    {
      path: '/ask',
      component: createListView('AskView')
    },
    // ...
  ]
})

위와 같은 방식으로 하이 오더 컴포넌트를 router에서 임포트 하고, 각 컴포넌트의 이름만 정의를 해주면 컴포넌트의 기본 공용 로직인 mounted(), name을 가진 컴포넌트가 생성됩니다. 따라서, 컴포넌트마다 불 필요하게 반복되는 코드를 정의 하지 않아도 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <div>
    <list-item></list-item>
  </div>
</template>

<script>
import ListItem from '../components/ListItem.vue';

export default {
  components: {
    ListItem,
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
  <ul class="item_list">
    <li v-for="(item, i) in list" :key="i" :id="item.id">

      <template v-if="item.points">
        <div class="points">
          {{ item.points }}
          <small>points</small>
        </div>
      </template>

      <div class="ttl_wrap">
        
        <strong>
          <template v-if="$route.name !== 'ask'">
            <a :href="item.url" target="blank">{{ item.title }}</a>
          </template>
          <template v-else>
            <router-link :to="`/item/${item.id}`">{{ item.title }}</router-link>
          </template>
        </strong>

        <p class="info_txt">
          <span v-if=" item.type === 'job' " class="domain"><a :href="`http://${item.domain}`" target="blank">{{ item.domain }}</a></span>
          <span v-if="item.user"><router-link :to="`/user/${item.user}`"><i class="fa-solid fa-user"></i> {{ item.user }}</router-link></span>
          <span>{{ item.time_ago }}</span>
          <span>{{ item.comments_count }} comments</span>
        </p>

      </div>

    </li>
  </ul>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
  computed: {
    ...mapGetters(['list']),
  },
}
</script>

view devtools로 확인해보면 JobsView 라는 HOC가 생성된 것을 확인할 수 있다.

alt text JobsView 컴포넌트는 HOC다

HOC (하이 오더 컴포넌트)의 단점

  • 일반: 상위 - 하위
  • HOC: 상위 - HOC - 하위

HOC를 이용하여 컴포넌트를 컴포넌트를 개발해 나가는 경우, 상위와 하위 컴포넌트 로직은 변경하지 않은 채 기능을 확장해 나갈 수 있습니다. 하지만 컴포넌트의 레벨이 깊어지고, 그로 인해 컴포넌트 간 통신이 불편해는 단점이 있습니다.



참고
This post is licensed under CC BY 4.0 by the author.