Custom-Component | Cannot access 'store' before initialization - vue.js

I want to access the vuex store inside my custom-component. I create the component like:
import {
defineCustomElement
} from 'vue';
import expensemain from '~/components/expense_editor/Main.ce.vue';
const CustomElement = defineCustomElement(expensemain);
window.customElements.define('expense-custom', CustomElement);
And import the store like:
import store from "../../store/store.js";
export default {
props: {
data: JSON,
expense_voucher_data: JSON
},
setup(props) {
let store = store.state.expense;
console.log(store);
But can't access it cause it seems not to be initialized.
Inside the store.js it is, tho:
const store = createStore({
modules: {
signup,
expense
}
});
export default store;
I can't use app.use inside main.js cause it's a custom component. How would I import the store to be able to use it?

I nearly got it right. The solution was as simple as it could've been:
import store from "../../store/store.js"; // import created store
export default {
props: {
data: JSON,
expense_voucher_data: JSON
},
setup(props) {
store.state.moduleName // direct access to module state
store.getters // getters
store.dispatch // actions
store.commit // mutations

Please try:
import store from "../../store/store.js";
import { useStore } from 'vuex'
export default {
props: {
data: JSON,
expense_voucher_data: JSON
},
setup(props) {
// let store = store.state.expense;
const store = useStore();
console.log(store);
And if it dosen't work, you'd better check Vuex guide of Composition API.

Related

How does Vuex 4 createStore() work internally

I am having some difficulty understanding how Veux 4 createStore() works.
In /store/index.js I have (amongst a few other things):
export function createVuexStore(){
return createStore({
modules: {
userStore,
productStore
}
})
}
export function provideStore(store) {
provide('vuex-store', store)
}
In client-entry.js I pass the store to makeApp() like this:
import * as vuexStore from './store/index.js';
import makeApp from './main.js'
const _vuexStore = vuexStore.createVuexStore();
const {app, router} = makeApp({
vuexStore: _vuexStore,
});
And main.js default method does this:
export default function(args) {
const rootComponent = {
render: () => h(App),
components: { App },
setup() {
vuexStore.provideStore(args.vuexStore)
}
}
const app = (isSSR ? createSSRApp : createApp)(rootComponent);
app.use(args.vuexStore);
So, there is no store that is exported from anywhere which means that I cannot import store in another .js file like my vue-router and access the getters or dispatch actions.
import {store} '../store/index.js' // not possible
In order to make this work, I did the following in the vue-router.js file which works but I don't understand why it works:
import * as vuexStore from '../store/index.js'
const $store = vuexStore.createVuexStore();
async function testMe(to, from, next) {
$store.getters('getUser'); // returns info correctly
$store.dispatch('logout'); // this works fine
}
Does Veux's createStore() method create a fresh new store each time or is it a reference to the same store that was created in client-entry.js? It appears it is the latter, so does that mean an application only has one store no matter how many times you run createStore()? Why, then, does running createStore() not overwrite the existing store and initialise it with blank values?
createStore() method can be used on your setup method.
On your main.js, you could do something like this
import { createApp } from 'vue'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
store.js
import { createStore } from 'vuex';
export default createStore({
state: {},
mutations: {},
actions: {},
});
To access your store, you don't need to import store.js anymore, you could just use the new useStore() method to create the object. You can directly access your store using it just as usual.
your-component.js
<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const isAuthenticated = computed(() => store.state.isAuthenticated);
const logout = () => store.dispatch("logout");
return { isAuthenticated, logout };
},
};
</script>
To use your store in the route.js file, you could simply imported the old fashion way.
route.js
import Home from '../views/Home.vue'
import store from '../store/'
const logout = () => {
store.dispatch("auth/logout");
}
export const routes = [
{
path: '/',
name: 'Home',
component: Home
}
]

Vuex 4 Modules can't use global axios property

I have a Vue3 (without Typescript) app running Vuex 4.0.0.
I'm trying to set up a simple GET request in one of my store modules using axios, however I'm getting a Cannot read property of 'get' of undefined when I try to do it via an action called in one of my components, however if I call this.$axios from the component it works fine. For some reason, my modules can't use this.$axios, while elsewhere in the app I can.
I've declared $axios as a globalProperty in my main.js file.
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { router } from "./router";
import { store } from "./store";
import axios from "axios";
const app = createApp(App).use(store).use(router);
app.config.globalProperties.$axios = axios;
app.mount("#app");
And the store module looks like this (simplified for the purposes of this question):
// store/modules/example.js
const state = () => ({
message: ""
});
const mutations = {
getMessage(state, payload) {
state.message = payload;
}
};
const actions = {
async setMessage(commit) {
this.$axios.get("example.com/endpoint").then(response => {
commit("setMessage", response.message);
});
}
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
The main store/index.js that's getting imported in main.js above looks like this:
// store/index.js
import "es6-promise";
import { createStore } from "vuex";
import example from "./modules/example";
export const store = createStore({ modules: { example } });
In the component, I have the following:
// MyComponent.vue
import { mapGetters, mapActions } from "vuex";
export default {
computed: {
...mapGetters({
message: "example/getMessage"
})
},
methods: {
...mapActions({
getMessage: "example/setMessage"
})
}
};
And a simple button with #click="getMessage". Clicking this button, however, returns Uncaught (in promise) TypeError: Cannot read property 'get' of undefined, but if I copy/paste the setMessage action into a component method, it works just fine.
Is there a way for me to expose this.$axios to the store files?
While this is not the ideal solution, as I would've preferred to have my $axios instance available globally with a single declaration in the mount file, it's probably the next best thing.
I made a lib/axiosConfig.js file that exports an axios instance with some custom axios options, and I just import that one instance in every module that needs it.
import axios from "axios";
axios.defaults.baseURL= import.meta.env.DEV ? "http://localhost:8000": "example.com";
axios.defaults.headers.common["Authorization"] = "Bearer " + localStorage.getItem("token");
axios.defaults.headers.common["Content-Type"] = "application/json";
// etc...
export const $axios = axios.create();
And in whatever module I need $axios in, I just import { $axios } from "./lib/axiosConfig. It's not perfect as I mentioned, since I do still have to import it in every module, but it's close enough as far as I can see, and has the added benefit of using the same axios config everywhere by just importing this file.

Is it possible to access VueX Getters during dynamic component registration

Is it possible to access VueX store getters when defining a dynamic component using webpack?
I'm using multiple modules inside my store.
Example:
components: {
'some-template': () => {
const someVal = this.$store.getters.someVal;
return System.import(`./some-template/${someVal}.vue`)
}
}
Main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import { someStore } from ./stores/some-store'
import { otherStore } from ./stores/other-store'
new Vue({
el: '#app',
store: new VueX.Store({
modules: {
someStore,
otherStore
}
})
})
Store example:
export const someStore = {
state: {
someVal: 'blah'
},
getters: {
someVal(state) {
return state.someVal;
}
}
}
To work around the issue with modules, I instead created an app store that will default using new Vuex instance, then dynamically add modules as needed. That way, I can import the store where needed and access the getters

vuex unknown action type when attempting to dispatch action from vuejs component

I'm using laravel, vue and vuex in another project with almost identical code and it's working great. I'm trying to adapt what I've done there to this project, using that code as boilerplate but I keep getting the error:
[vuex] unknown action type: panels/GET_PANEL
I have an index.js in the store directory which then imports namespaced store modules, to keep things tidy:
import Vue from "vue";
import Vuex from "vuex";
var axios = require("axios");
import users from "./users";
import subscriptions from "./subscriptions";
import blocks from "./blocks";
import panels from "./panels";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
actions: {
},
mutations: {
},
modules: {
users,
subscriptions,
blocks,
panels
}
})
panels.js:
const state = {
panel: []
}
const getters = {
}
const actions = {
GET_PANEL : async ({ state, commit }, panel_id) => {
let { data } = await axios.get('/api/panel/'+panel_id)
commit('SET_PANEL', data)
}
}
const mutations = {
SET_PANEL (state, panel) {
state.panel = panel
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Below is the script section from my vue component:
<script>
import { mapState, mapActions } from "vuex";
export default {
data () {
return {
}
},
mounted() {
this.$store.dispatch('panels/GET_PANEL', 6)
},
computed:
mapState({
panel: state => state.panels.panel
}),
methods: {
...mapActions([
"panels/GET_PANEL"
])
}
}
</script>
And here is the relevant code from my app.js:
import Vue from 'vue';
import Vuex from 'vuex'
import store from './store';
Vue.use(Vuex)
const app = new Vue({
store: store,
}).$mount('#bsrwrap')
UPDATE:: I've tried to just log the initial state from vuex and I get: Error in mounted hook: "ReferenceError: panel is not defined. I tried creating another, very basic components using another module store, no luck there either. I checked my vuex version, 3.1.0, the latest. Seems to be something in the app.js or store, since the problem persists across multiple modules.
Once you have namespaced module use the following mapping:
...mapActions("panels", ["GET_PANEL"])
Where first argument is module's namespace and second is array of actions to map.

How to watch two more stores in Vue.js and Vuex?

I'm using Vue.js and Vuex.
I want to watch the value of store (I'll share my sample codes in the last of this post).
But I have encountered the error "Error: [vuex] store.watch only accepts a function."
In this Web site, I found how to use "single" store.
https://codepen.io/CodinCat/pen/PpNvYr
However, in my case, I want to use "two" stores.
Do you have any idea to solve this problem.
●index.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
import {test1Store} from './modules/test1.js';
import {test2Store} from './modules/test2.js';
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
test1: test1Store,
test2: tes21Store,
}
});
●test1.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const checkerStore = {
namespaced: true,
state: {
count: 1
},
getters: {
getCount(state){
return state.count;
}
}
};
export default {test1};
●test.vue
<template>
{{ $store.state.couunt }}
</template>
<script>
import {store} from './store/index.js';
export default {
data: function () {
return {
}
},
store: store,
methods: {
},
mounted() {
setInterval(() => { this.$store.state.count++ }, 1000);
this.$store.watch(this.$store.getters['test1/getCount'], n => {
console.log('watched: ', n)
})
}
}
}
</script>
You're getting that error because in your example code, the first argument you've passed to watch is, as the error suggests, not a function.
The argument is actually implemented using the native JavaScript call Object.defineProperty underneath the hood by Vue, so the result of doing store.getters['test1/getCount'] as you have, is actually a number.
I don't think it's recommended to use watch in a scenario like yours, but if you insist, you can access count by providing an alternate argument form, something like:
this.$store.watch((state, getters) => getters['test1/getCount'], n => { ... })