본문 바로가기
front/vue

[vue3 공식문서 번역]Components.6.Provide/inject

by juniKang 2022. 5. 4.

Prop Derilling

보통, 부모에서 자식으로 데이터를 보낼 때, props를 사용한다. 하지만, 거대한 컴포넌트 트리를 가지고 있는 경우, 깊게 중첩된 컴포넌트는 먼 조상 컴포넌트로 부터 뭔가를 받아올 필요가 있을 때가 있다. props만으로는, 전체 부모 체인에서 같은 prop을 넘겨야할것이다.

<Footer> 컴포넌트가 모든 props를 신경쓰지는 않지만, <DeppChild>가 접근할 수 있게 그것들을 넘기고 선언할 필요가 있다. 만약 부모체인이 더 길다면, 더 많은 컴포넌트들이 이런 방법의 영향을 받을 것이다. 이걸 "props drilling" 이라고 부르고, 이걸 다루는건 분명 즐겁지 않다.

 

props derilling을 provide와 inject로 해결할 수 있다. 부모 컴포넌트는 모든 후손을들 위해 dependency provider를 제공할 수 있다. 후손 트리의 모든 컴포넌트는, 얼마나 깊은지 상관없이, 부모체인의 컴포넌트가 제공하는 의존성들을 inject할 수 있다.

Provide

컴포넌트 후손에게 데이터를 제공하기 위해, provide() 함수를 사용한다:

<script setup>
import { provide } from 'vue'

//provide(key,value)
provide('message', 'hello!')
</script>

<script setup>을 사용하지 않으면, provide()는 동기적으로 setup()안에서 호출된다:

import { provide } from 'vue'

export default {
  setup() {
    provide('message', 'hello!')
  }
}

provide() 함수는 두 개의 아규먼트를 받는다. 첫번째는 Symbol이나 문자열인 injection key 이다.injection key는 inject하기를 희망하는 값을 찾기위해 후손 컴포넌트가 사용한다. 한 컴포넌트에서 다른 값을 제공하기 위해 다른 injection keys들을 여러번 provide()를 호출로 제공할 수 있다.

 

두번째 아규먼트는 provided value이다. 값은 refs같은 반응형 상태를 포함해서 모든 타입이 될 수 있다:

import { ref, provide } from 'vue'

const count = ref(0)
provide('key', count)

reactive values를 제공하는 것은 provided value를 사용하는 후손 컴포넌트가 제공하는 컴포넌트와 반응형 커넥션을 갖도록 해준다.

 

App-level Provide

컴포넌트에 데이터를 제공하는 것에 더해, 앱레벨에서도 또한 제공할 수 있다:

import { createApp } from 'vue'

const app = createApp({})
app.provide('message', 'hello!')

앱레벨 제공은 앱에서 렌더링되는 모든컴포넌트에 가능하다. plugins을 사용할 때 특히 유용한데, 플러그인들이 컴포넌트를 사용해 값을 제공하는건 일반적으로 불가능하기 때문이다.

 

Inject

조상컴포넌트로부터 제공된 데이터를 inject하기 위해, inject() 함수를 사용한다:

<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

만약 제공되는 값이 ref라면, 다음과 같이 injected 될 것이다-자동으로 언래핑되지는 않을것이다. injector component(주입받는 컴포넌트)가 provider component(제공하는 컴포넌트)로 reacitivity connection을 유지하도록 해준다.

 

Full provide + inject Example with Reacitivity

 

다시, <script setup>을 사용하지 않으면, inject()는setup()안에서 동기적으로 호출될 수밖에 없다:

import { inejct } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

 

Injection Default Values

기본적으로, inject는 injected key가 부모 체인의 어딘가에서 제공되는 경우를 상정한다. 키가 제공되지 않는 경우, 런타임 경고가 발생한다.

 

만약 주입받은 프로퍼티가 선택적인 providers와 작동하길 원하면, default value를 선언해야한다. props와 비슷하다:

// 'value'는 'default value' 가 된다.
// '만약 'messagge'와 일치하는게 없으면 제공된다
const value = inject('message', 'default value')

떄로, default value가 함수를 호출하거나 새로운 클래스를 인스턴스화 하는 일에 필요할 수 있다. 그 경우에 불필요한 사이드 이펙트나 computation을 피하기 위해, 선택적인 값은 사용되지 않고, default value를 만들기 위해 팩토리 함수를 사용할 수 있다:

const value = inject('key', () => new ExpensiveClass())

 Working with Reactivity

reactive provide/ inject value를 사용할 때, 가능한 모든 상황에서 모든 상태변화들이 프로바이더의 내부 상태가 반응형이도록 유지하는걸 추천한다. 제공받은 상태와 가능한 상태변이들이 같은 컴포넌트에 같이 위치해서, 추후에 유지하기 쉽게 만든다.

 

injector 컴포넌트로부터 받은 데이터를 업데이트해야할 때가 있다. 그럴 때, 상태를 변화시킬 책임이있는 함수를 제공하는걸 추천한다:

<!-- inside provider component -- > 
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
<!-- in injector component -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation"{{ location }}</button>
</template>

마침내, injected component에서 provide로 제공받은 데이터가 상태변화할 수 없도록 보장하고 싶을 때, provided value를 readonly()로 wrap할 수 있다. 

<script setup>
import { ref, proivde, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>

Working with Symbol Keys

지금까지, 예제에서 문자열 injection keys를 사용해왔다. 많은 의존성을 제공하는 큰 애플리케이션에서 일하거나, 다른 개발자들에게 사용될 컴포넌트를 제작중이면, Symbol injection keys가 잠재적인 충돌을 피하는 가장 좋은 방법이다.

 

dedicated file에서 Symbols을 expord 하는걸 추천한다:

// keys.js
export const myInjectionKey = Symbol()
// in provider component
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  // data to provide
})
// in injector component
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

See also: Typing Provide / Inject

댓글