Since Vue.js 3.x, you no longer need to use Vuex to share a centralized state with all your application's components.
A brief history of shared state in Vue.js
If you have been using Vue.js before 3.x you are probably familiar with Vuex now renamed Pinia. It allows you to have a centralized state that is shared across all your application components. If you have ever found yourself in situations where you are passing data/props between components and things are getting complicated, a centralized state is the answer.
Flux libraries are like glasses: you’ll know when you need them.
The simplest way to share the state between components.
Since Vue.js 3.x reactive
and computed
are exported directly from the library and can be used outside of components. This is the exact same functionality that you are using when you have data
or computed
inside of one of your Vue.js components.
import { reactive, computed } from 'vue';
This is great because it means that we can create our own reactive data, and even computed properties outside of Vue components, in plain Javascript files!
Example of shared state
import { reactive, computed } from "vue";
const state = reactive({
hobby: "Hike",
fruit: "Apple",
fruitOptions: ["Banana", "Apple", "Pear", "Watermelon"],
description: computed(() => {
return `I like eating ${state.fruit}s & ${state.hobby}`;
}),
});
export default state;
Here we have an Object with properties, which is wrapped in reactive
this gives Vue the ability to trigger change, and re-render your components.
Computed
We are also wrapping a function in computed
which gives us a computed property based on the the other fields in the state.
It is important to note, that we invoke the reactive function, and THEN export the resulting state, otherwise we would create a new state each time we imported the file.
Usage in your components
Great, we have a reactive store, but how do we leverage this in our components, and will they all stay in sync?
<template>
<main class="container">
<article>
<header>
Your description: <code>{{ state.description }}</code>
</header>
<survey-form />
<footer>
<results />
</footer>
</article>
</main>
</template>
<script>
import Results from "./Results.vue";
import SurveyForm from "./SurveyForm.vue";
import state from "./state.js";
export default {
components: { SurveyForm, Results },
data() {
return { state };
},
};
</script>
It's as simple as importing the state, and referencing it in your component's data method so it can be accessed in your template.
You can even v-model
items in your store the same as you would with any data property in your component.
<template>
<form>
<label>
Favorite Hobby?
<input placeholder="Favorite Hobby" v-model="state.hobby" />
</label>
<label>
Favorite Fruit?
<select v-model="state.fruit">
<option v-for="fruit in state.fruitOptions" :key="fruit">{{ fruit }}</option>
</select>
</label>
</form>
</template>
<script>
import state from "./state.js";
export default {
name: "SurveyForm",
data() {
return { state };
},
};
</script>
You can see the full example on Github and view the code here
I hope you find this pattern as useful as I have. Happy developing!
Top comments (4)
This is interesting. Does this work in larger projects like for example an admin based project ie role based access control kind of project, crud applications
It would scale as well as reactive data in a component does, as its the same logic being used in both places. The main advantage of a flux lib like Vuex IMO is that you can organize logic in different modules. I suppose you could do something similar with this pattern, but it could get messy if you were not careful.
Oh thanks. The link was helpful. I will be working on an admin based crud application with Casl package for ability -based among other things. Appreciate your feedback and response 🙏
Very good, since the release of vue 3 I have never needed vuex/pinia in my projects.