Home > Net >  How to link a shared store with multiple single file components in Vue.js 3?
How to link a shared store with multiple single file components in Vue.js 3?

Time:11-06

I have a Vue.js 3 app. This app includes multiple single file components. I'm trying to figure out how to implement the simplest global state management. The docs make sense to me. However, the limited docs show a reactive object returned from the data function of multiple components. This example assumes the reactive object and components are all in the same file. The challenge I'm running into is using a shared store across multiple files. Currently I have:

  • app.vue
  • component-a.vue
  • component-b.vue
  • store.js

These files looks like this:

app.vue

<template>
    <div>        
      <div>How much money do you have?</div>
      <input type="number" v-model="currentAmount" />
      <br />
      <div>How much money do you want?</div>
      <input type="number" v-model="desiredAmount" />
    </div>
</template>

<script>
  import ComponentA from './component-a.vue';
  import ComponentB from './component-b.vue';

  export default {
     components: {
       ComponentA,
       ComponentB
     },

     data() {
         return {
           currentAmount: 0,
           desiredAmount: 0
         }
     },
  }
</script>

component-a.vue

<template>
    <div>        
      <div>You have {{ remainingAmount }} to reach your goal.</div>
    </div>
</template>

<script>
  export default {
     data() {
         return {
           remainingAmount: 0
         }
     },
  }
</script>

component-b.vue

<template>
    <div>        
      <div>You are {{ progress }}% of the way there!</div>
      <button @click="increaseGoal">increase goal by $10</button>
    </div>
</template>

<script>
  export default {
     data() {
         return {
           progress: 0
         }
     },

     methods: {
        increaseGoal() {
           // need to increase targetAmount by $10
           store.targetAmount  = 10; 
        }
     }
  }
</script>

store.js

import { reactive} from 'vue';

const store = reactive({
  startingAmount: 0,
  targetAmount: 0
});

The UI renders. However, clearly the data is not getting propagated across the components. I can clearly see that there's not a link between store and the three .vue files. There's nothing that ties the store with the UI. I don't know how to get the store.js associated with the three .vue files properly though.

How do you link a shared store with multiple single file components? Please note: I do not want to use Vuex.

CodePudding user response:

To get hold of the store on a global level you can make vue use() it so that you will be able to call this.$store from any component within the VueApp

main.js

import { createApp } from 'vue';
import store from './store/index'; // OR the path to your
import router from './router/index';
import App from './App.vue';


createApp(App).use(router).use(store).mount('#app');

I created a store-folder because I use the store within multiple modules. In the store folder is the index.js which hold the basics store and all modules.

store/index.js

import { createStore } from 'vuex';

const store = createStore({
  modules: {},
  state() {
    return {};
  },
  mutations: {},
  actions: {},
  getters: {},
});

export default store;

By doing that you can call the store from any component.

component.vue

export default {
  name: 'users-list',
  created() {
    console.log(this.$store);
  },
}

The variables in your store's state should not be altered directly.

The store has 3 types of procedures.

Actions - Do elaborated Stuff


The Actions are used to do the heavy work of a given change in data. This is where you would cross-feed the function from different store modules, make axios calls and similar. Therefore Actions is the only procedure group that can access the store it-self. the first argument is basically the whole store. Actions are triggered by calling dispatch('NameOfModule/NameOfAction', ValuetoPass).

Mutations - Beloved Setter Functions


The Mutations are used to mutate a state, and ONLY Mutations should mutate the state in order to keep reactivity and workflows clean. The Mutations therefore only get passed their own local state as first parameter. Mutations will be triggered by commit('NameOfMutation', ValueToPass)

Getters - Pretty self-explanatory, isn't it?


The getters are used to, well yeah, get States.

CodePudding user response:

As you said, you don't need to use Vuex to create global state. Thanks to composition-api we can use composables.

First, create directory /composables and add javascript file (it's good practice to create file beginning with use word) useState.js:

import { reactive, toRefs } from "vue";

const state = reactive({
  isMenuOpened: false
});

const toggleMenuState = () => {
  state.isMenuOpened = !state.isMenuOpened;
};

export default {
  ...toRefs(state),
  toggleMenuState
};

toRefs converts all of the properties, to a plain object with properties that are refs

Now you can use composable in vue components:

<script>
import useState from "./composables/useState";

export default {
  setup() {
    const { isMenuOpened, toggleMenuState } = useState;

    return {
      isMenuOpened,
      toggleMenuState,
    };
  },
};
</script>

Demo: https://codesandbox.io/s/happy-chandrasekhar-o05uv?file=/src/App.vue

About composition api and composables: https://v3.vuejs.org/guide/composition-api-introduction.html

  • Related