I would like to clear user data upon logout, so from auth module I dispatch an action which is defined in a module ride but I am getting an error:
[vuex] unknown local action type: clearUserData, global type: auth/clearUserData
This is my code:
store/modules/auth.js
export const namespaced = true
export const mutations = {
clearAuthData(state){
state.apiToken = null
state.signedIn = false
}
}
export const actions = {
logout({ commit, dispatch }) {
commit('clearAuthData'))
dispatch('ride/clearUserData', { root: true })
}
}
store/modules/ride.js
export const namespaced = true
export const state = {
data: []
}
export const mutations = {
CLEAR_USER_DATA(state){
state.data = []
}
}
export const actions = {
clearUserData({ commit }) {
commit('CLEAR_USER_DATA')
}
}
store/store.js
import * as auth from './modules/auth'
import * as ride from './modules/ride'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
loading: false
},
modules: {
auth,
ride
}
})
What am I doing wrong?
Your usage of dispatch() is incorrectly passing { root: true } as the 2nd argument, which is intended for the payload. Options should be 3rd:
// dispatch('ride/clearUserData', { root: true }) // FIXME: options passed as payload
dispatch('ride/clearUserData', null, { root: true })
If you don't have payload to pass, then you need to send the second parameter as a null like:
dispatch('ride/clearUserData', null, { root: true })
Related
Login.vue
<script setup>
import { useLayout } from '#/layout/composables/layout';
import { ref, computed } from 'vue';
import AppConfig from '#/layout/AppConfig.vue';
import { decodeCredential } from 'vue3-google-login'
import {auth} from '../../../store/modules/auth.module';
import { useStore } from "vuex";
const store = useStore()
const { layoutConfig, contextPath } = useLayout();
const email = ref('');
const password = ref('');
const checked = ref(false);
const logoUrl = computed(() => {
return `${contextPath}layout/images/${layoutConfig.darkTheme.value ? 'logo-white' : 'logo-dark'}.svg`;
});
const callback = (response) => {
const userData = decodeCredential(response.credential);
// const authStore = auth;
// console.log(authStore.login());
if (userData.email=='****#gmail.com') {
return store.dispatch('login')
}
}
</script>
auth.module.js
import AuthService from "../../services/auth.service";
const user = JSON.parse(localStorage.getItem('token'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, user) {
return AuthService.login(user).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
},
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
}
};
auth.service.js
import axios from 'axios';
const API_URL = 'http://localhostGetToken';
class AuthService {
async login(user) {
const response = await axios
.post(API_URL, {
username: user.username='admin',
password: user.password='password'
});
if (response.data.accessToken) {
localStorage.setItem('token', JSON.stringify(response.token));
}
console.log(response);
return response.data;
}
async logout() {
localStorage.removeItem('token');
}
}
export default new AuthService();
Here i trying to login if email true to trigger login vuex.but i get a error [vuex] unknown action type: login
how to solve this?
You haven't included in your question how the auth store is linked to your application.
I'm guessing you have a main store and the auth store is one of its modules.
If my guess is true, you should dispatch auth/login, not login, since the main store doesn't have a login action.
Side note: I suggest you carefully read How to Ask, to improve the quality of your future questions.
The problems with your current question:
you posted too much irrelevant code and, at the same time, you haven't posted all the relevant code. You should have included:
a) the action deemed unknown (everything else in that store is irrelevant for this question)
b) how the store is linked to the app (main store + how the store is instantiated in the app) - these bits are missing
c) how you're consuming the action in the component (everything else in the component is irrelevant for the question)
you started with the code. Always start by explaining the problem, so when people look at the code, they know what to look for (and skip the irrelevant parts). This is also helpful for future users with a similar problem: they'll be able to quickly understand if your question is relevant for their problem.
The more users find the question useful, the more chances for it to get upvoted.
Another side-note: the condition used to dispatch is, most likely, wrong. It is only true when the email is actually '****#gmail.com'.
You should probably use if (userData.email.endsWith('#gmail.com')).
When a user updates their username in the EditAccount component, the username is updated in the EditAccount component and in vuex store but not in the Navigation component even though stage change is updated to the new user name.
The problem is that the user is seing thier old user name in Navigation component and a updated user name in the EditAccount component and they don't match.
How can I Re render the Navigation component with the new user name?
Below is the the code for user the data in the Navigation component.
Store vuex: index.js
const store = createStore({
// strict: true,
state: {
user: null,
authIsReady: false,
//
// current category
playlistCategory: null,
},
//
getters: {
getUser(state) {
return state.user;
},
},
mutations: {
//
// update playlist category
updatePlaylistCategory(state, payload) {
state.playlistCategory = payload;
},
//
//
setUser(state, payload) {
state.user = payload;
},
//
setAuthIsReady(state, payload) {
state.authIsReady = payload;
},
//
},
actions: {
async editUser(context, payload) {
const { displayNewName, displayNewEmail } = payload;
await updateUserDetails(displayNewName, displayNewEmail);
// get current user
const responseUser = await user;
// set user state
context.commit('setUser', responseUser);
},
},
NavBar.vue
// vue3 and composition api
setup() {
// store
const store = useStore()
//
const { error, logout, isPending } = useLogout()
const router = useRouter()
//
// getters
const user = computed(() => {
return store.getters.getUser.displayName
})
Try adding set and get property:
const user = computed({
get: store.state.user,
set: (val) => store.state.user = val
});
Try using a getter instead acessing the value directly in the state
Getter for user:
export function getUser(state){
return state.getUser
}
and in the component import the getter like this:
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters('*theStoreName*',['getUser'])
},
watch: {
getUser: function(){
//Should be possible to see when the getUser changes here
console.log(this.getUser)
}
}
}
</script>
Note: You have theStoreName for the store name you're using
Maybe the problem is that the store name is missing, or when you did store.state.user you're acessing the store? If it is it, then you should try to inform the variable you're trying to access, like If it is, like store.state.user.name, with the getter it would be: getUser.name
Created a new service .js called room.module.js
and inside my Vue view I have the following event on form submit:
submit: function(e) {
e.preventDefault();
var name = this.$refs.name.value;
var capacity = this.$refs.places.value;
// dummy delay
setTimeout(() => {
// send update request
this.$store.dispatch(CREATE_ROOM, {
"name": name,
"places": capacity
});
}, 2000);
and my service room.module.js:
// action types
import ApiService from "#/core/services/api.service";
import JwtService from "#/core/services/jwt.service";
export const CREATE_ROOM = "createNewRoom";
// mutation types
export const SET_ROOM_INFO = "setRoomInfo";
const state = {
room_info: {
name: "Room 1",
places: 10,
status: 1
}
};
const getters = {
currentRoomInfo(state) {
return state.room_info;
}
};
const actions = {
[CREATE_ROOM](context, payload) {
if (JwtService.getToken()) {
ApiService.setHeader();
ApiService.put("/room/create", payload).then(({ data }) => {
context.commit(SET_ROOM_INFO, payload);
return data;
});
}
}
};
const mutations = {
[SET_ROOM_INFO](state, room_info) {
state.room_info = room_info;
}
};
export default {
state,
actions,
mutations,
getters
};
but when I submit the form, the following error occurs:
[vuex] unknown action type: createNewRoom
I know I'm missing something, but can't figure out what.
Any ideas? Thank you!
It seems it needs to be added to the Vuex store.
I have a reusable Vuex module for CRUD methods to an API. How can I pass the relevant URL from the parent module when loading?
e.g.
company.module.js
var URL;
const state = {
all: [],
items: [],
editing: false,
url: URL
};
...
export default {
state,
getters,
actions,
mutations,
modules: {
crud: crudModule
}
};
crud.module.js
const state = {
url: '', // Get the URL from company.module here?
status: {
loading: false,
success: false,
error : false
}
};
...
const actions = {
async fetchItems(context, data ) {
commit('QUERY_REQUEST');
var url = '';// Or get the URL from parent state?
return axios.get( url )
.then( response => {
...
});
},
}
...
export default {
namespaced: true,
modules: {
meta: metaModule
},
state: () => state,
getters,
mutations,
actions
};
I figured it out. Instead of a module, I put the crud state, getters, mutations and actions into a class with the endpoint as a parameter. I can then use the Crud class in each of my namespaced modules and merge it using the spread operator.
crud.js
export default class {
constructor ( endpoint ) {
this.state = {
url: endpoint,
status: {
loading: false,
success: false,
error : false
}
};
this.getters = getters;
this.mutations = mutations;
this.actions = actions;
}
}
const getters = {
//...
};
const actions = {
//...
};
const mutations = {
//...
};
company.module.js
import Crud from './api/crud';
let endpoint = "/api/companies";
var crud = new Crud( endpoint );
const state = {
...crud.state
};
const getters = {
...crud.getters
};
const actions = {
...crud.actions
};
const mutations = {
...crud.mutations
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
this is going to work.
async fetchItems({commit, rootGetters }, data ) {
commit('QUERY_REQUEST');
let url = rootGetters['company/url']
}
link to official docs: accessing-global-assets-in-namespaced-modules
I am running ESLint and I am currently running into the following ESLint error:
error 'state' is already declared in the upper scope no-shadow
const state = {
date: '',
show: false
};
const getters = {
date: state => state.date,
show: state => state.show
};
const mutations = {
updateDate(state, payload) {
state.date = payload.date;
},
showDatePicker(state) {
state.show = true;
}
};
export default {
state,
getters,
mutations
};
What would be the best way to fix this?
The best way to fix would be to read the docs about the eslint "no-shadow" rule.
From this documentation, the best solution would probably be to include an exception for this one variable with the "allow" option.
You can add this with a comment to the js file to keep the exeption local:
/* eslint no-shadow: ["error", { "allow": ["state"] }]*/
The best solution is #Linus Borg's answer.
If you are looking for an alternative, you can declare the state constant below the rest. This will prevent variable shadowing because state will not be declared in the outer-scope yet.
Example:
const getters = {
date: state => state.date,
show: state => state.show
};
const mutations = {
updateDate(state, payload) {
state.date = payload.date;
},
showDatePicker(state) {
state.show = true;
}
};
const state = {
date: '',
show: false
};
export default {
state,
getters,
mutations
};
If it's not too late
const data = {
date: '',
show: false
};
const getters = {
date: state => state.date,
show: state => state.show
};
const mutations = {
updateDate(state, payload) {
state.date = payload.date;
},
showDatePicker(state) {
state.show = true;
}
};
export default {
state: data,
getters,
mutations
};
basically you define your store data as data, and you export it as state state: data
Had the same issue as I was using an airbnb eslint config which is incompatible with vuex.
This worked for me, after restarting dev environment.
I created a new .eslintrc.js file in my store folder and added them there
"no-shadow": ["error", { "allow": ["state"] }],
"no-param-reassign": [
"error",
{
"props": true,
"ignorePropertyModificationsFor": [ // All properties except state are in the ignorePropertyModificationsFor array by default.
"state",
"acc",
"e",
"ctx",
"req",
"request",
"res",
"response",
"$scope"
]
}
],
Based on #allochi's answer, this is what I had to do to make it work With Vue 3 which uses Vuex 4 which prefers returning a function for state:
// store.js
const data = {
// ...
};
const getters = {
// ...
};
const mutations = {
// ...
};
const actions = {
// ...
};
export default {
state() { return data; },
getters,
mutations,
actions
};
If you need to import particular functions from outside, you will have to do it like this:
import mystore from './mystore';
const Store = createStore({
state: mystore.state,
getters: mystore.getters,
mutations: mystore.mutations,
actions: mystore.actions
});
I would only recommend this though if you really can't use /* eslint no-shadow: ["error", { "allow": ["state"] }]*/