Skip to content

Commit 223eac0

Browse files
authored
docs (#391): update provide inject in composition api with enhanced reactivity (#396)
* docs: use maps example in provide inject composition * docs: update code sample to show object * docs (#391): enhance provide inject reactivity section * docs: update disclaimer for provide inject composition api * docs: fix imports * docs: remove unnecessary qualifier * docs: fix grammar in disclaimer * docs: integrate tip into content
1 parent b2a0c91 commit 223eac0

File tree

1 file changed

+230
-71
lines changed

1 file changed

+230
-71
lines changed

src/guide/composition-api-provide-inject.md

Lines changed: 230 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,280 @@
11
# Provide / Inject
22

3-
> This guide assumes that you have already read the [Composition API Introduction](composition-api-introduction.html) and [Reactivity Fundamentals](reactivity-fundamentals.html). Read that first if you are new to Composition API.
3+
> This guide assumes that you have already read [Provide / Inject](component-provide-inject.html), [Composition API Introduction](composition-api-introduction.html), and [Reactivity Fundamentals](reactivity-fundamentals.html).
44
55
We can use [provide / inject](component-provide-inject.html) with the Composition API as well. Both can only be called during [`setup()`](composition-api-setup.html) with a current active instance.
66

7-
For example, if we want to provide a book name on the root component and inject it on the child component:
7+
## Scenario Background
88

9-
```js
10-
import { provide, inject } from 'vue'
9+
Let's assume that we want to rewrite the following code, which contains a `MyMap` component that provides a `MyMarker` component with the user's location, using the Composition API.
1110

12-
const RootComponent = {
11+
```vue
12+
<!-- src/components/MyMap.vue -->
13+
<template>
14+
<MyMarker />
15+
</template>
16+
17+
<script>
18+
import MyMarker from './MyMarker.vue'
19+
20+
export default {
21+
components: {
22+
MyMarker
23+
},
24+
provide: {
25+
location: 'North Pole',
26+
geolocation: {
27+
longitude: 90,
28+
latitude: 135
29+
}
30+
}
31+
}
32+
</script>
33+
```
34+
35+
```vue
36+
<!-- src/components/MyMarker.vue -->
37+
<script>
38+
export default {
39+
inject: ['location', 'longitude', 'latitude']
40+
}
41+
</script>
42+
```
43+
44+
## Using Provide
45+
46+
When using `provide` in `setup()`, we start by explicitly importing the method from `vue`. This allows us to define each property with its own invocation of `provide`.
47+
48+
The `provide` function allows you to define the property through two parameters:
49+
50+
1. The property's name (`<String>` type)
51+
2. The property's value
52+
53+
Using our `MyMap` component, our provided values can be refactored as the following:
54+
55+
```vue{7,14-20}
56+
<!-- src/components/MyMap.vue -->
57+
<template>
58+
<MyMarker />
59+
</template>
60+
61+
<script>
62+
import { provide } from 'vue'
63+
import MyMarker from './MyMarker.vue
64+
65+
export default {
66+
components: {
67+
MyMarker
68+
},
1369
setup() {
14-
provide('book', 'Vue 3 guide')
70+
provide('location', 'North Pole')
71+
provide('geolocation', {
72+
longitude: 90,
73+
latitude: 135
74+
})
1575
}
1676
}
77+
</script>
78+
```
79+
80+
## Using Inject
81+
82+
When using `inject` in `setup()`, we also need to explicitly import it from `vue`. Once we do so, this allows us to invoke it to define how we want to expose it to our component.
83+
84+
The `inject` function takes two parameters:
1785

18-
const MyBook = {
86+
1. The name of the property to inject
87+
2. A default value (**Optional**)
88+
89+
Using our `MyMarker` component, we can refactor it with the following code:
90+
91+
```vue{3,6-14}
92+
<!-- src/components/MyMarker.vue -->
93+
<script>
94+
import { inject } from 'vue'
95+
96+
export default {
1997
setup() {
20-
const book = inject(
21-
'book',
22-
'Eloquent Javascript' /* optional default value */
23-
)
98+
const userLocation = inject('location', 'The Universe')
99+
const userGeolocation = inject('geolocation')
100+
24101
return {
25-
book
102+
userLocation,
103+
userGeolocation
26104
}
27105
}
28106
}
107+
</script>
29108
```
30109

31-
`inject` accepts an optional default value as the 2nd argument. If a default value is not provided and the property is not found on the provide context, `inject` returns `undefined`.
110+
## Reactivity
111+
112+
### Adding Reactivity
113+
114+
To add reactivity between provided and injected values, we can use a [ref](reactivity-fundamentals.html#creating-standalone-reactive-values-as-refs) or [reactive](reactivity-fundamentals.html#declaring-reactive-state) when providing a value.
115+
116+
Using our `MyMap` component, our code can be updated as follows:
32117

33-
If we need to provide or inject multiple values, we can do this with a subsequent call of `provide` or `inject` respectively:
118+
```vue{7,15-22}
119+
<!-- src/components/MyMap.vue -->
120+
<template>
121+
<MyMarker />
122+
</template>
34123
35-
```js{5-6,12-16}
36-
import { provide, inject } from 'vue'
124+
<script>
125+
import { provide, reactive, ref } from 'vue'
126+
import MyMarker from './MyMarker.vue
37127
38-
const RootComponent = {
128+
export default {
129+
components: {
130+
MyMarker
131+
},
39132
setup() {
40-
provide('book', 'Vue 3 guide')
41-
provide('year', '2020')
133+
const location = ref('North Pole')
134+
const geolocation = reactive({
135+
longitude: 90,
136+
latitude: 135
137+
})
138+
139+
provide('location', location)
140+
provide('geolocation', geolocation)
42141
}
43142
}
143+
</script>
144+
```
145+
146+
Now, if anything changes in either property, the `MyMarker` component will automatically be updated as well!
147+
148+
### Mutating Reactive Properties
149+
150+
When using reactive provide / inject values, **it is recommended to keep any mutations to reactive properties inside of the _provider_ whenever possible**.
151+
152+
For example, in the event we needed to change the user's location, we would ideally do this inside of our `MyMap` component.
153+
154+
```vue{28-32}
155+
<!-- src/components/MyMap.vue -->
156+
<template>
157+
<MyMarker />
158+
</template>
44159
45-
const MyBook = {
160+
<script>
161+
import { provide, reactive, ref } from 'vue'
162+
import MyMarker from './MyMarker.vue
163+
164+
export default {
165+
components: {
166+
MyMarker
167+
},
46168
setup() {
47-
const book = inject(
48-
'book',
49-
'Eloquent Javascript' /* optional default value */
50-
)
51-
const year = inject('year')
169+
const location = ref('North Pole')
170+
const geolocation = reactive({
171+
longitude: 90,
172+
latitude: 135
173+
})
174+
175+
provide('location', location)
176+
provide('geolocation', geolocation)
52177
53178
return {
54-
book,
55-
year
179+
location
180+
}
181+
},
182+
methods: {
183+
updateLocation() {
184+
this.location = 'South Pole'
56185
}
57186
}
58187
}
188+
</script>
59189
```
60190

61-
## Injection Reactivity
62-
63-
To retain reactivity between provided and injected values, we can use a [ref](reactivity-fundamentals.html#creating-standalone-reactive-values-as-refs) or [reactive](reactivity-fundamentals.html#declaring-reactive-state) when providing a value:
191+
However, there are times where we need to update the data inside of the component where the data is injected. In this scenario, we recommend providing a method that is responsible for mutating the reactive property.
64192

65-
```js
66-
import { ref, reactive } from 'vue'
193+
```vue{21-23,27}
194+
<!-- src/components/MyMap.vue -->
195+
<template>
196+
<MyMarker />
197+
</template>
67198
68-
// in provider
69-
setup() {
70-
const book = reactive({
71-
title: 'Vue 3 Guide',
72-
author: 'Vue Team'
73-
})
74-
const year = ref('2020')
199+
<script>
200+
import { provide, reactive, ref } from 'vue'
201+
import MyMarker from './MyMarker.vue
75202
76-
provide('book', book)
77-
provide('year', year)
78-
}
203+
export default {
204+
components: {
205+
MyMarker
206+
},
207+
setup() {
208+
const location = ref('North Pole')
209+
const geolocation = reactive({
210+
longitude: 90,
211+
latitude: 135
212+
})
79213
80-
// in consumer
81-
setup() {
82-
const book = inject('book')
83-
const year = inject('year')
214+
const updateLocation = () => {
215+
location.value = 'South Pole'
216+
}
84217
85-
return { book, year }
218+
provide('location', location)
219+
provide('geolocation', geolocation)
220+
provide('updateLocation', updateLocation)
221+
}
86222
}
223+
</script>
87224
```
88225

89-
Now, when either `book` or `year` are changed on the _provider_ component, we can observe them changing on the component where they are injected.
226+
```vue{9,14}
227+
<!-- src/components/MyMarker.vue -->
228+
<script>
229+
import { inject } from 'vue'
230+
231+
export default {
232+
setup() {
233+
const userLocation = inject('location', 'The Universe')
234+
const userGeolocation = inject('geolocation')
235+
const updateUserLocation = inject('updateUserLocation')
90236
91-
::: warning
92-
We don't recommend mutating a reactive property where it's injected as it's breaking Vue one-direction data flow. Instead, try to either mutate values where they are _provided_ or provide a method to mutate them
237+
return {
238+
userLocation,
239+
userGeolocation,
240+
updateUserLocation
241+
}
242+
}
243+
}
244+
</script>
245+
```
93246

94-
```js
95-
import { ref, reactive } from 'vue'
247+
Finally, we recommend using `readonly` on provdided property if you want to ensure that the data passed through `provide` cannot be mutated by the injected component.
96248

97-
// in provider
98-
setup() {
99-
const book = reactive({
100-
title: 'Vue 3 Guide',
101-
author: 'Vue Team'
102-
})
249+
```vue{7,25-26}
250+
<!-- src/components/MyMap.vue -->
251+
<template>
252+
<MyMarker />
253+
</template>
103254
104-
function changeBookName() {
105-
book.title = 'Vue 3 Advanced Guide'
106-
}
255+
<script>
256+
import { provide, reactive, readonly, ref } from 'vue'
257+
import MyMarker from './MyMarker.vue
107258
108-
provide('book', book)
109-
provide('changeBookName', changeBookName)
110-
}
259+
export default {
260+
components: {
261+
MyMarker
262+
},
263+
setup() {
264+
const location = ref('North Pole')
265+
const geolocation = reactive({
266+
longitude: 90,
267+
latitude: 135
268+
})
111269
112-
// in consumer
113-
setup() {
114-
const book = inject('book')
115-
const changeBookName = inject('changeBookName')
270+
const updateLocation = () => {
271+
location.value = 'South Pole'
272+
}
116273
117-
return { book, changeBookName }
274+
provide('location', readonly(location))
275+
provide('geolocation', readonly(geolocation))
276+
provide('updateLocation', updateLocation)
277+
}
118278
}
279+
</script>
119280
```
120-
121-
:::

0 commit comments

Comments
 (0)