부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 Props를 사용했었다.
그런데 위와 같이 Props의 깊이가 깊다고 가정해보자.
그것도 아주 많이 깊다고 가정해보면, 계속해서 Props로 전달하는 것은 굉장히 불편할 것이다. 중간에 있는 컴포넌트들은 그저 Props를 전달하기 위해 선언해야 할 것이다.
이렇게 하위 컴포넌트들이 전달하는 용도로만 쓰이는 과정을 Prop Drilling이라고 한다.
이것을 해결할 수 있는 것이 바로 Provide와 Inject이다.
Provide와 Inject를 사용하면, 부모 컴포넌트는 계층구조에 상관없이 자식 컴포넌트에 데이터를 전달할 수 있다.
import { provide, ref } from 'vue';
export default {
setup() {
const message = ref('Hello World!');
provide('message', message);
return {
message,
};
},
};
문자열
또는 Symbol
. 주입 키는 하위 컴포넌트에서 주입된 값을 조회하는 데 사용된다.import { inject } from 'vue';
export default {
setup() {
const message = inject('message');
const appMessage = inject('appMessage');
return {
message,
appMessage,
};
},
};
Inject를 통해 상위 컴포넌트에서 제공한 데이터를 삽입할 수 있다.
주입된 값이 ref이면 반응형 연결을 유지할 수 있다.
만약에 Inject로 주입된 키가 상위 체인에서 제공된 적이 없는 경우 런타임 경고가 표시된다. 이 때 두번째 인자로 기본값(default value)를 설정할 수 있다.
const defaultMessage = inject('defaultMessage', 'default message');
기본값으로 팩토리 함수를 제공할 수도 있다.
const defaultMessage = inject('defaultMessage', () => 'default message');
Provide/Inject를 반응성 데이터로 제공할 때 가능한 모든 변경을 Provider 내부에서 하는 것이 좋다. 예를 들면 자식 컴포넌트에서 주입받은 값을 변경하고 싶으면, Injector 내부 컴포넌트에서 변경하지 말고 Provider에서 데이터 변경 함수를 함께 제공하는 것이 좋다.
// Provider
const message = ref('Hello World!');
const updateMessage = () => {
message.value = 'world!';
};
provide('message', { message, updateMessage });
// Injector
const { message, updateMessage } = inject('message');
이렇게 Provider 내부에 배치되면 향후 유지관리가 용이하다.
주입된 컴포넌트에서 제공된 값을 변경할 수 없도록 하려면 readonly() 함수를 사용할 수 있다.
import { provide, readonly, ref } from 'vue';
provide('count', readonly(count));
대규모 애플리케이션에서 다른 개발자와 함께 작업할 때 잠재적 충돌을 피하기 위해 Symbol 주입 키를 사용하는 것이 가장 좋다.
// 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)
컴포넌트에서 데이터를 제공하는 것 외에도 App-level에서 제공할 수도 있다.
App컴포넌트는 모든 자식 컴포넌트의 부모 컴포넌트이므로 App-level에서 제공하면 모든 컴포넌트에서 받아 쓸 수 있다.
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.provide('appMessage', 'Hello app message');
app.mount('#app');
이것은 Plugin을 작성할 때 유용하다.
Vue2에서 컴포넌트 인스턴스 객체를 추가할 때 global property에 추가 했으나, Vue3에서 Composition API Setup 함수에서는 컴포넌트 인스턴스에 접근할 수 없다.
이때 대신 Provide/Inject를 사용할 수 있다.
vue공식문서 Provide/Inject
인프런 Vue3기본편 - 짐코딩