owned this note
owned this note
Published
Linked with GitHub
---
tags: vue3
---
# Vue3 - Composition API - Provide / Inject
### [예시 - 코드샌드박스 by yejineee](https://codesandbox.io/s/composition-api-provide-inject-ef4ms?file=/src/components/MyMarker.vue)
# 🍒 Provide / inject

## provide/inject의 필요성
기존에는 부모에서 자식 컴포넌트로 데이타를 전달해야할 경우엔 `props`를 사용하였다. 만약, 컴포넌트 구조가 복잡하게 중첩되어있을 경우엔, drilling으로 데이타를 전달해야 하며, 이 작업은 매우 짜증난다...
이러한 문제를 해결하기 위해 `provide`, `inject`를 사용한다. 부모 컴포넌트가 provider가 되어 그 하위의 모든 자식들에게 데이타를 전달하게 된다. 부모는 `provide` option으로 데이타를 전달하게 되고, 자식 컴포넌트는 `inject` option으로 데이타를 사용하게 된다.
(리액트의 Context API가 이러한 역할을 한다. Context.Provider / Context.Consumer가 provide/inject에 대응한다.)
## 예시
부모 컴포넌트 쪽에서 provide로 데이터를 넘긴다. 이 때, **provide는 객체를 넘기는 함수인데, 이렇게 해야 component instance property를 자식 컴포넌트에게 넘길 수 있다.**
```javascript
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
provide() {
return {
todoLength: this.todos.length
}
},
template: `
...
`
})
```
```javascript
app.component('todo-list-statistics', {
inject: ['todoLength'],
created() {
console.log(`Injected property: ${this.todoLength}`) // > Injected property: John Doe
}
})
```
만약, component instance property가 아닌 값을 넘겨줄 때는 provide는 다음과 같은 형태여도 된다.
```javascript
provide: {
location: "North Pole",
geolocation: {
longitude: 90,
latitude: 135,
},
},
```
## provide/inject에 반응형 추가하기
**기본적으로 provide/inject는 reactive하지 않다**. 따라서, todos 배열의 길이가 달라져도, 주입된 todoLength property에는 반영되지 않는다.
반응형을 추가하고 싶으면
- **ref** property 전달 or
- **reactive** object 전달
을 해야 한다.
여기서는 Composition API의 `computed` 프로퍼티를 todoLength에 추가하였다.
injected property를 가져올 때는 this.todoLength.value로 가져온 것을 알 수 있다.
```javascript
app.component('todo-list', {
// ...
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
})
app.component('todo-list-statistics', {
inject: ['todoLength'],
created() {
console.log(`Injected property: ${this.todoLength.value}`) // > Injected property: 5
}
})
```
# 🍒 Composition API - provide/inject
Composition API에서 provide, inject를 사용할 수 있다.
## Provide 사용하기
setup()안에서 provide를 사용하기 위해서는 vue에서 provide를 import해야 한다.
provide 함수로는 두 값을 전해주어야 한다.
- name (String type)
- value
```javascript
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
```
## Inject 사용하기
setup()안에서 inject 사용하려면 vue에서 import 해와야 한다.
inject함수는 두 파라미터를 받는다.
- inject할 프로퍼티 이름
- default value(optional)
```javascript
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
```
## 반응형 추가하기
**반응형을 추가하기 위해선 `ref`나 `reactive`** 를 provide할 value에 추가해야 한다.
```javascript
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script>
```
## 반응형 프로퍼티 변경하기
- provider안에서 값 변경하기 : 가능한한 그렇게 할 것 !
**reactive property를 변경하는 것은 provider 안에서 하는 것을 추천한다.**
```javascript
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
return {
location
}
},
methods: {
updateLocation() { // provider안에서 location property 변경하는 메소드 추가
this.location = 'South Pole'
}
}
}
</script>
```
- inject한 컴포넌트에서 값 변경하기
만약, 데이타를 inject받은 컴포넌트에서 데이타를 변경해야 할 경우도 있을 것이다. 이럴 때는, **reactive property를 변경할 메소드를 provider쪽에서 전달할 것을 추천**한다.
```javascript
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
}
}
</script>
```
- **`readonly` 사용하여 상수화하기**
**값이 변경되어서는 안될 프로퍼티에는 `readonly`를 provider 쪽에서 추가**하여, injected component 쪽에서 값을 변경시키지 못하도록 할 것을 추천한다.
```javascript
<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>
```