I am using Vuex with axios to fetch data from my backend. But somehow the state property userName is not updating in my Vue Single File Component(SFC).
approot.js
state
const state = {
userName: 'foo'
};
getter
const getters = {
getUserName: (state) => state.userName
};
Single File Component
<template>
<div id="navbar">
//cut for brievity
<span>{{getUserName}}</span>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: 'navbar',
computed: mapGetters(['getNumberOfJobMessages','getUserName']),
//cut for brievity
}
</script>
<style scoped>
//cut for brievity
</style>
Action fetching data with axios from the backend
const actions = {
async fetchMenuData({ commit }) {
//fetch data from api controller
const response = await axios.get('../api/Menu/GetMenu');
console.log(response.data.userName); //not undefined
commit('setMenuData', response.data);
}
}
Mutation setting state variables
const mutations = {
setMenuData(state, menuData) {
console.log(menuData.userName); //not undefined
state.userName = menuData.userName;
console.log(state.userName); //not undefined
}
}
Problem
When my single file component calls getUserName it always renders 'foo', the hardcoded value. Im quite baffled by this, since the rest of my state variables are set with the same pattern, and my components have no problems getting them.
Anyone who knows whats going wrong or can see a flaw in my code? It would be highly appreciated.
Use mutations to only set data. and other things do on action. like:
Action:
const actions = {
async fetchMenuData({ commit }) {
const response = await axios.get('../api/Menu/GetMenu');
let userName = response.data.userName;
commit('setUserName', userName);
}
}
And mutations:
const mutations = {
setUserName(state, userName) {
state.userName = userName;
}
}
Dont forget to dispatch the function fetchMenuData
Properly not sure, why this happens. But, I faced this problem and solved by this way.
axios.get('../api/Menu/GetMenu')
.then(({ data }) => {
commit('setUserName', data.userName);
}).catch(error => { })
It is better to make a commit in then()
Related
I might have missed something super obvious when refactoring my implementation of Redux in a React application, but when I'm trying to access the value of one of my slices I get thrown some errors by the Typescript Compiler about not being able to assign a (func) => string to a parameter of type string.
For context, here's my implementation:
Slice:
export const environmentSlice = createSlice({
name: 'environment',
initialState,
reducers: {
updateEnvironment: (state, action:PayloadAction<string>) => {
state.value = action.payload
}
}
});
export const { updateEnvironment } = environmentSlice.actions;
export const selectEnvironment = (state: RootState) => state.environment.value;
How i've defined the interface for my environment:
// Defining type for state
interface EnvironmentState {
value: string,
};
// define the initial state using that type
const initialState: EnvironmentState = {
value: 'live',
}
How RootState is defined in my store:
export const store = configureStore({
reducer: {
loggedIn: loggedInReducer,
environment: environmentReducer,
token: tokenReducer,
},
})
export type RootState = ReturnType<typeof store.getState>;
How I'm trying to get the value into one of my React Components:
let environment = useAppSelector((state: RootState) => {
return state.environment.value
});
I've also tried following the implementation in the redux docs here but had no luck with that: https://react-redux.js.org/tutorials/typescript-quick-start#use-typed-hooks-in-components
When assigning this value, i'm using useAppDispatch() assigned to a variable inside of the response section of a fetch request:
fetch('/api/authenticate', requestOptions)
.then(async response => {
if (response.status === 200) {
let data = await response.json();
dispatch({ type: toggle });
dispatch({ type: updateToken, payload: data.token });
webHelpers.get('/api/user', 'default', 'auth', data.token, (data: any) => {
dispatch({ type: updateUser, payload: data.full_name })
});
//
navigate('../management/staff');
Please note: The environment isn't updated upon sign-in but only once the user selects an option from a drop-down menu in the DOM. It's directly after this sign-in and navigation that the application crashes, however, as it states it cannot read the 'value' on the following:
const token = useAppSelector(state => {
return state.token.value
});
The above is reached after the navigate('../management/staff'); is called.
Edit: Accidently included wrong code snippet when showing useAppSelector in use. Update to fix.
Edit2: Added in section about the dispatches that assigns these values.
Edit3: Managed to resolve the solution but not in the exact way I'd hoped so I'll leave this open. The issue appeared to be that the attempts to dispatch data via the slices I'd added to my store's reducer didn't work, having all of those methods on one sole slice resolved the issue. This isn't ideal as I'd wanted 3 separate slices to manage each of these states separately. There must be some issue in my redux store with setting these up to work independently.
I found something about this bug I explained at end;
Component codes
async fetch(){ await this.$store.dispatch('bots/getBots') },
computed: { ...mapState('bots', ['bots']) },
Store codes
export const state = () => {
return {
bots: []
}
}
export const mutations = {
UPDATE_BOTS(state, bots) {
state.bots = bots
}
}
export const actions = {
getBots({commit}) {
this.$axios.$get('url', {headers: {uid: '12345'}})
.then(res => {
commit('UPDATE_BOTS',res.robots)
})
.catch(e => {
console.log(e)
})
}
}
Issue: When moving between pages via nuxt-link data loads perfectly but when I reload the page bots state is empty...
Found Issue:
I use nuxt-auth and I had one plugin for checking status of axios request that if it was 401 unauthorized I logout user if he was loggedIn, So status undefined error was from here but I commented the plugin codes and I got other error from nuxt-auth that causes that problem I had So I related that issue in other question u can see it here:
Nuxt-Auth Bug: Looks for autherization in any get request that has headers config
It is the expected behavior. Vuex state is kept in memory and when you reload the page it gets purged.
Instead of this state
export const state = () => {
return {
bots: []
}
}
try this
export const state = () => ({
bots: []
})
I have a vuex store module called login.js as below
import axios from "axios";
import router from "#/router";
axios.defaults.baseURL = process.env.VUE_APP_API_ENDPOINT;
const state = {
access_token: localStorage.getItem("access_token") || null,
};
const getters = {
loggedIn() {
return (
state.access_token != null && localStorage.getItem("access_token") != null
);
}
};
const mutations = {
doLogin(state, response) {
const token = response.authentication_data.access_token;
localStorage.setItem("access_token", token);
state.access_token = token;
router.push("/admin");
};
const actions = {
async getToken({ commit }, userdata) {
let email = userdata.email;
let password = userdata.password;
let remember_me = userdata.remember_me;
await axios
.post("auth/login", null, {
params: {
email,
password,
remember_me
}
})
.then(response => {
if (response.data.meta.status == "true") {
commit("doLogin", response.data);
} else {
alert("wrong password");
}
})
.catch(error => {
alert(error);
});
};
export default {
state,
getters,
actions,
mutations
};
login.vue code
methods: {
...mapActions(["getToken"]),
login() {
const userdata = {
email: this.email,
password: this.password,
remember_me: true
};
this.getToken(userdata);
}
}
The login function works and the token is set for the 1st time but when I refresh the browser the access_token is gone.
In the browser, it's shown as below
But if I commit via dev tools it works and the state becomes persistent.
The similar nature questions on SO but don't answer this question.
vuex commit does not commit to store
Vue2 + Vuex Commit Not Committing (without Vue devtools)
Vuex Mutation running, but component not updating until manual commit in vue dev tools
How can I make the state.access_token persistent via the code? The problem is with a page refresh I lost state.access_token value.
Your code is fine and Vuex is successfully "committing" your data to the store. The issue you're experiencing is coming from the fact that Vuex (out of the box) does not persist your data in localStorage, which I believe is what you mean by "commit". As has been mentioned a couple times in comments on your question, you are going to need to use a third party package (most people use Vuex-PersistedState, but I prefer Vuex-Persist as it's more customizable and supports Typescript). Either one is very easy to get started with.
With Vuex-PersistedState, you'll need to update your Vuex initialization with the new plugin. It should look something like this:
import createPersistedState from 'vuex-persistedstate' // import the package
const store = new Vuex.Store({
plugins: [createPersistedState()] /// include the imported plugin
})
I'm working on a static website fetching content from the WordPress API.
On the menu of the website, I want the content to be save on a nuxt store, and available on the nav component.
I reed the doc of the nuxt server and the nuxtServerInit action, but I didn't find a nice example of how to make a axion call inside this action, and be able to fetch the store on the component.
I find this, but it's not working .. https://github.com/nuxt/nuxt.js/issues/2307
Thanks a lot for your help.
Try this
store/index.js
export const state = () => ({
data: null
})
export const actions = {
// nuxtServerInit is called by Nuxt.js before server-rendering every page
async nuxtServerInit({ commit, dispatch }) {
await dispatch('storeDispatchFunc')
},
// axios...
async storeDispatchFunc({ commit }) {
const { data } = await this.$axios.get('/api/wp....')
commit('SET_DATA', data)
},
}
export const mutations = {
SET_DATA(state, theData) {
state.data = theData
}
}
I have a React, Redux app which should work as a CRUD application. And a part of a CRUD application is the ability to update stuff and that's what I currently have trouble with. The PUT request works (can see the changes in Robomongo), but my app crashes afterwards and the problem lies in my reducer; Unhandled Rejection (TypeError): Cannot read property 'item' of undefined (yeah, item is not the best naming, sorry).
I'd like to walk you through the process of the PUT request, because code > text after all.
I will start where my action is created, because I guess you can figure out I have a form as my legit starting point.
So, here's my action (sorry for the wall of code)
Action:
import axios from 'axios'
import settings from '../../../../settings'
import { merge } from 'lodash'
axios.defaults.baseURL = settings.hostname
export function updateSettings(id, updatedValues, controller, door) {
const custom_name = updatedValues.custom_name
const location = updatedValues.location
const open_duration = updatedValues.open_duration
return (dispatch, getState) => {
const state = getState()
const door = state.fetchDoors.doors.find(val => val._id === id.itemId)
const controller = state.fetchDoors.controllers.find(
controller => controller._id === door.controller
)
console.log('door', door) // Returns updated object
console.log('controller', controller) // Returns updated object
const doorPayload = {
...door,
custom_name,
location
}
const controllerPayload = {
...controller,
open_duration
}
axios
.put(`${settings.hostname}/locks/${id.itemId}`, doorPayload)
.then(res => {
dispatch({ type: 'DOOR_UPDATING' })
dispatch({
type: 'DOOR_UPDATED_SUCCESS',
doorPayload
})
})
axios
.put(
`${settings.hostname}/controllers/${door.controller}`,
controllerPayload
)
.then(res => {
dispatch({ type: 'CONTROLLER_UPDATING' })
dispatch({
type: 'CONTROLLER_UPDATING_SUCCESS',
controllerPayload
})
})
.catch(err => console.log(err))
}
}
And here's my reducer
Reducer:
const initialState = {
isLoading: false
}
export const settings = (state = initialState, action) => {
switch (action.type) {
case 'DOOR_UPDATING':
return { ...state, isLoading: true }
case 'DOOR_UPDATED_SUCCESS':
return { ...state, item: action.payload.item, isLoading: false } // Here's where the error occurs
case 'CONTROLLER_UPDATING':
return { ...state, isLoading: true }
case 'CONTROLLER_UPDATING_SUCCESS':
return { ...state, item: action.payload.item, isLoading: false }
default:
return state
}
}
So the error occur inside of my reducer (I've added a comment) and I really don't understand why, now when the PUT request changes the data inside of my database. I assume there's something silly I'm missing, but I can't fix it. All help is really appreciated and if more code/ info needed just let me know.
Thanks for reading.
Edit:
Here's how my door object looks like:
In your reducer you are expecting and action with the shape of:
{type: 'something', payload: 'something else'}
But when you dispatch the action you don't have a property of payload.
this is what you are dispatching:
{
...door, // this will spread all properties of door (which doesn't have a property with the name payload)
custom_name,
location
}
Then you are trying to access action.payload.item hence you get the error:
Cannot read property 'item' of undefined
payload is never defined in your action (by the way nor item was).