|
1 | 1 | # Provide / Inject
|
2 | 2 |
|
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). |
4 | 4 |
|
5 | 5 | 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.
|
6 | 6 |
|
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 |
8 | 8 |
|
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. |
11 | 10 |
|
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 | + }, |
13 | 69 | setup() {
|
14 |
| - provide('book', 'Vue 3 guide') |
| 70 | + provide('location', 'North Pole') |
| 71 | + provide('geolocation', { |
| 72 | + longitude: 90, |
| 73 | + latitude: 135 |
| 74 | + }) |
15 | 75 | }
|
16 | 76 | }
|
| 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: |
17 | 85 |
|
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 { |
19 | 97 | 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 | +
|
24 | 101 | return {
|
25 |
| - book |
| 102 | + userLocation, |
| 103 | + userGeolocation |
26 | 104 | }
|
27 | 105 | }
|
28 | 106 | }
|
| 107 | +</script> |
29 | 108 | ```
|
30 | 109 |
|
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: |
32 | 117 |
|
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> |
34 | 123 |
|
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 |
37 | 127 |
|
38 |
| -const RootComponent = { |
| 128 | +export default { |
| 129 | + components: { |
| 130 | + MyMarker |
| 131 | + }, |
39 | 132 | 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) |
42 | 141 | }
|
43 | 142 | }
|
| 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> |
44 | 159 |
|
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 | + }, |
46 | 168 | 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) |
52 | 177 |
|
53 | 178 | return {
|
54 |
| - book, |
55 |
| - year |
| 179 | + location |
| 180 | + } |
| 181 | + }, |
| 182 | + methods: { |
| 183 | + updateLocation() { |
| 184 | + this.location = 'South Pole' |
56 | 185 | }
|
57 | 186 | }
|
58 | 187 | }
|
| 188 | +</script> |
59 | 189 | ```
|
60 | 190 |
|
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. |
64 | 192 |
|
65 |
| -```js |
66 |
| -import { ref, reactive } from 'vue' |
| 193 | +```vue{21-23,27} |
| 194 | +<!-- src/components/MyMap.vue --> |
| 195 | +<template> |
| 196 | + <MyMarker /> |
| 197 | +</template> |
67 | 198 |
|
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 |
75 | 202 |
|
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 | + }) |
79 | 213 |
|
80 |
| -// in consumer |
81 |
| -setup() { |
82 |
| - const book = inject('book') |
83 |
| - const year = inject('year') |
| 214 | + const updateLocation = () => { |
| 215 | + location.value = 'South Pole' |
| 216 | + } |
84 | 217 |
|
85 |
| - return { book, year } |
| 218 | + provide('location', location) |
| 219 | + provide('geolocation', geolocation) |
| 220 | + provide('updateLocation', updateLocation) |
| 221 | + } |
86 | 222 | }
|
| 223 | +</script> |
87 | 224 | ```
|
88 | 225 |
|
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') |
90 | 236 |
|
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 | +``` |
93 | 246 |
|
94 |
| -```js |
95 |
| -import { ref, reactive } from 'vue' |
| 247 | +Finally, we recommend using `readonly` on provided property if you want to ensure that the data passed through `provide` cannot be mutated by the injected component. |
96 | 248 |
|
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> |
103 | 254 |
|
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 |
107 | 258 |
|
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 | + }) |
111 | 269 |
|
112 |
| -// in consumer |
113 |
| -setup() { |
114 |
| - const book = inject('book') |
115 |
| - const changeBookName = inject('changeBookName') |
| 270 | + const updateLocation = () => { |
| 271 | + location.value = 'South Pole' |
| 272 | + } |
116 | 273 |
|
117 |
| - return { book, changeBookName } |
| 274 | + provide('location', readonly(location)) |
| 275 | + provide('geolocation', readonly(geolocation)) |
| 276 | + provide('updateLocation', updateLocation) |
| 277 | + } |
118 | 278 | }
|
| 279 | +</script> |
119 | 280 | ```
|
120 |
| - |
121 |
| -::: |
|
0 commit comments