Preface
A variety of ways to manage and share state, including the most commonly used Vuex. However, in some cases, we only need simple data sharing between parent and child components without introducing a complete global state management solution. This is a lightweight way to achieve this through the provide and inject APIs.
This article will provide and inject patterns in detail and explore its advanced usage in real-world applications.
What is Provide/Inject?
Provide and Inject are provided pairs of APIs for providing and injecting dependencies in the component tree. Through this pair of APIs, the parent component can provide data and the child component can inject this data, avoiding unnecessary props passing by the intermediate layer components. Simply put, provide is to "provid data" and inject is to "receive data".
Basic usage
Provide data (Provide)
Use the provide option in the parent component to provide data. provide can be an object or a function that returns an object.
// <template> <div> <ChildComponent /> </div> </template> <script> export default { name: 'ParentComponent', provide() { return { message: 'Hello from Parent' } } } </script>
In this example, ParentComponent provides a message data through provide.
Inject data
Use the inject option in the child component to receive data.
// <template> <div> {{ message }} </div> </template> <script> export default { name: 'ChildComponent', inject: ['message'] } </script>
In this example, ChildComponent receives message data through inject and displays it in the template.
Advanced Usage
1. Use objects as Provide
Sometimes we need to provide multiple data, and we can directly return an object in the provide:
// <template> <div> <ChildComponent /> </div> </template> <script> export default { name: 'ParentComponent', provide() { return { message: 'Hello from Parent', user: { name: 'John Doe', age: 30 } } } } </script>
2. Use factory functions to dynamic data
Sometimes, we need to provide data dynamically according to the state of the parent component, and then we can use factory functions:
<template> <div> <ChildComponent /> </div> </template> <script> export default { name: 'ParentComponent', data() { return { parentMessage: 'Hello from reactive Parent' } }, provide() { return { message: } } } </script>
3. Combined with responsive data
It should be noted that the data provided through the provide is not responsive. If the data you want to provide is responsive, you can use Vue's reactive or ref.
<template> <div> <ChildComponent /> </div> </template> <script> import { ref } from 'vue'; export default { name: 'ParentComponent', setup() { const message = ref('Hello from reactive Parent'); return { message, provide: { message } } } } </script>
// <template> <div> {{ message }} </div> </template> <script> import { inject } from 'vue'; export default { name: 'ChildComponent', setup() { const message = inject('message'); return { message } } } </script>
4. Optional injection
Sometimes, the data that the child component may wish to receive is optional. That is, if data is not provided, the child component can use the default value. We can use object syntax in inject to set default values.
// <template> <div> {{ message }} </div> </template> <script> export default { name: 'ChildComponent', inject: { message: { from: 'message', default: 'Default Message' } } } </script>
In this example, if the parent component does not provide a message, the child component will use ‘Default Message’ as the default value.
5. Use in deep components
The data of provide and inject can be used at any deep level in the component tree. The data provided by the parent component can be used by child components and child components of the child component, etc.
// <template> <div> <ChildComponent /> </div> </template> <script> export default { name: 'ParentComponent', provide() { return { message: 'Hello from Parent' } } } </script>
// <template> <div> <GrandchildComponent /> </div> </template> <script> export default { name: 'ChildComponent' } </script>
// <template> <div> {{ message }} </div> </template> <script> export default { name: 'GrandchildComponent', inject: ['message'] } </script>
In this example, even if GrandchildComponent is a grand component of ParentComponent, it can still receive the message provided by ParentComponent.
6. Processing of responsive data
As mentioned earlier, by default, the data provided is not responsive. If we need the data to be responsive, we can use the Vue Composition API to implement it.
// <template> <div> <ChildComponent /> <button @click="updateMessage">Update Message</button> </div> </template> <script> import { ref, provide } from 'vue'; export default { name: 'ParentComponent', setup() { const message = ref('Hello from Parent'); provide('message', message); const updateMessage = () => { = 'Updated Message from Parent'; }; return { updateMessage }; } } </script>
// <template> <div> {{ message }} </div> </template> <script> import { inject } from 'vue'; export default { name: 'ChildComponent', setup() { const message = inject('message'); return { message }; } } </script>
In this example, clicking the button will update the value of the message and the update will be automatically reflected in the ChildComponent. This is because we use ref to make message responsive data.
Actual cases
Topic Switch
Suppose we want to implement a simple theme switching function where users can switch between dark mode and bright mode. We can use provide and inject to achieve it.
1. Topic Provider Components
Create a topic provider component that provides the current topic and a way to switch topics to the child components through provide.
// <template> <div :class="theme"> <slot></slot> <button @click="toggleTheme">Toggle Theme</button> </div> </template> <script> import { ref, provide } from 'vue'; export default { name: 'ThemeProvider', setup() { const theme = ref('light'); const toggleTheme = () => { = ( === 'light' ? 'dark' : 'light'); }; provide('theme', theme); provide('toggleTheme', toggleTheme); return { theme, toggleTheme }; } } </script> <style> .light { background-color: white; color: black; } .dark { background-color: black; color: white; } </style>
2. Theme Consumer Component
Create a child component, injecting and switching themes from ThemeProvider.
// <template> <div> <p>The current theme is: {{ theme }}</p> </div> </template> <script> import { inject } from 'vue'; export default { name: 'ThemedComponent', setup() { const theme = inject('theme'); return { theme }; } } </script>
3. Application Components
Use ThemeProvider to wrap the ThemedComponent in the application.
// <template> <ThemeProvider> <ThemedComponent /> </ThemeProvider> </template> <script> import ThemeProvider from './components/'; import ThemedComponent from './components/'; export default { name: 'App', components: { ThemeProvider, ThemedComponent } } </script>
In this way, we implement a simple theme switching function, and all theme-related logic is concentrated in the ThemeProvider component, and the child components only need to inject relevant data.
Summarize
Overall, provide and inject are very powerful and flexible tools that can significantly simplify data sharing among components, especially in scenarios with deeper component levels. While they provide a convenient solution, they also need to be used with caution to avoid over-dependence resulting in reduced readability and maintainability of the code. In actual development, a reasonable combination of provide and inject, as well as other state management solutions (such as Vuex), can make the application more structural and maintainable.
This is the article about this article that will help you understand the use of Provide/Inject in Vue and advanced applications. For more related Vue Provide/Inject content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!