adding resolve() to vuex action breaks data loading in component - javascript

I have the following action in my VueX store
Adding resolve() makes component unable to load store data
fetch_resources({ commit, rootState }) {
return new Promise((resolve, reject) => {
const url = '/api/resource/';
rootState.axios_api.get(url)
.then((response) => {
commit('SET_RESOURCES', response.data);
resolve(response.data); // Adding this line breaks my component
}).catch((error) => {
reject(setErrorServer(error));
});
});
},
Data is loaded and mutated in the store but is not loaded whitin the component called by this action on mount.
How should I fix my then().catch() to make it work ?
mounted() {
this.fetch_resources().then(() => {
}).catch((error) => {
showErrorModal(error);
});
},
``̀̀̀`

Axios themself returns promise, so you dont need to wrap it:
fetch_resources({ commit, rootState }) {
const url = '/api/resource/';
// also you may make a closure to param variable
let com = commit
return rootState.axios_api.get(url)
.then((response) => {
commit('SET_RESOURCES', response.data);
// com('SET_RESOURCES', response.data);
resolve(response.data); // Adding this line breaks my component
}).catch((error) => {
reject(setErrorServer(error));
});
},

Related

How to wait for action to complete before accessing the Vue Store state?

I have Vuejs/Nuxtjs application within which I need to access a Vuex store state after it has been modified by Vuex action. Currently when I try to run the action and assignment then I get the old state and not the one which was updated after action.
How to make the code wait for action completion then run the next statement? Following is the code I have currently:
Vuejs Component:
<template>
<div>
<input v-model="formData.value" type="text">
<button #click="performAction">
Click Me
</button>
</div>
</template>
<script>
export default {
data () {
return {
formData: {
value: '',
returnValue: ''
}
}
},
methods: {
performAction () {
// Set the value within the Vuex Store
this.$store.commit('modules/DataStore/populateData', this.formData.value)
// Perform the Action
this.$store.dispatch('modules/DataStore/getData').then(() => {
console.log("AFTER COMPLETE ACTION")
})
// Assign the update value to the variable
this.formData.returnValue = this.$store.state.modules.DataStore.data
}
}
}
</script>
<style>
</style>
Vuex Store:
export const state = () => ({
data:''
})
export const mutations = {
populateData (state, data) {
state.data = data
}
}
export const actions = {
getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
this.$axios
.post('/getUrlData', state.data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data)
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
}
Following are the thing I tried and nothing is working at the moment:
I tried the .then() function.
I tried Async and await but both are not working
Any suggestions will be really appreciated. Thanks in advance.
You can create getter in vuex :
export const getters = {
getData: (state) => state.data,
};
export const actions = {
async setData ({ commit }, data) {
const headers = { 'Content-Type': 'application/json' }
await this.$axios
.post('/getUrlData', data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data)
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
}
then in component you can map getters and actions, and call them :
import { mapGetters, mapActions } from 'vuex'
computed: {
...mapGetters(['getData']),
},
methods: {
...mapActions(['performAction']),
async performAction() {
await this.setData(this.formData.value)
this.formData.returnValue = this.getData
}
}
You need to return your promise in your if you want to chain it in the calling method. eg:
getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
return this.$axios // now this promise will be returned and you can chain your methods together
.post('/getUrlData', state.data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data);
return response.data; //this will allow you do send the data through to the next Then() call if you want to
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
This situation is a lot easier to manage with async-await IMO. It becomes:
export const actions = {
async getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
const response = await this.$axios.post('/getUrlData', state.data, { headers });
console.log("WITHIN RESPONSE")
commit('populateData',response.data);
}
}
and
methods: {
async performAction () {
// Set the value within the Vuex Store
this.$store.commit('modules/DataStore/populateData', this.formData.value)
// Perform the Action
await this.$store.dispatch('modules/DataStore/getData');
console.log("AFTER COMPLETE ACTION");
// Assign the update value to the variable
this.formData.returnValue = this.$store.state.modules.DataStore.data
}
}

Vue store dispatch error response not being passed to UI

I'm trying to get the error response from my Vue store dispatch method, into my component, so I can tell the user if the save failed or not.
store/userDetails.js
const state = {
loading: {
user_details: false,
}
}
const getters = {
// Getters
}
const actions = {
save({commit, dispatch, rootState}, payload) {
commit('setLoading', {name: 'users', value: true});
axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
).then((response) => {
if (response.data) {
commit('setState', {name: 'user_details', value: response.data.units});
commit('setLoading', {name: 'user_details', value: false});
dispatch(
'CommonSettings/setSavingStatus',
{components: {userDetails: "done"}},
{root:true}
);
}
}).catch((error)=> {
console.log(error)
return error
}
)
}
My component method
views/Users.vue
send() {
this.$store.dispatch({
type: 'Users/save',
userDetails: this.current
}).then(response => {
console.log(response)
});
},
Above, I'm logging out the response in two places.
The response in my store/userDetails.js file is logged out fine, but it's not being passed to my send() function in my component - it comes up as undefined. Any reason why it wouldn't be passed through? Is this the correct way to do this?
This works for me. Try this solution.
store.js
actions: {
save(context, payload) {
console.log(payload);
return new Promise((resolve, reject) => {
axios(url)
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
},
My Component method
App.vue
save(){
this.$store.dispatch("save", dataSendToApi).then((response)=>{
console.log(response)
})
}
Try returning axios call in the Store Action:
// add return
return axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
)
.then() // your stuff here
.catch() // your stuff here
If that won't work, use Promise in the Store Action. Like this:
return new Promise((resolve, reject) => {
return axios() // simplify for readibility reason, do your stuff here
.then((response) => {
//... your stuff here
resolve(response) // add this line
})
.catch((error) => {
// ... your stuff here
reject(error) // add this line
})
})
you should return a promise, reference link:vue doc

Javascript - await fetch response

I have a React-Native app and when the component mounts, I want to fetch data by calling a method in our services class, wait for that data to be returned, then set that data in setState({}). But setState({}) is called before the data is returned.
//Component class
componentDidMount(){
this.getData();
}
async getData() {
const data = await MyService.getData();
this.setState({
blah:data //undefined
});
}
//Services Class
let MyService = {
getData:function(){
axios.get(url)
.then(response => response.data)
.then((data) => {
//do stuff
return data;//need to set this to state back in component class
})
.catch(error => {
console.log(error);
});
}
}
module.exports = MyService;
You have to return the axios.get call. Otherwise the async function will return an empty promise (promise with the undefined value).
let MyService = {
getData: function() {
return axios.get(url)
.then(response => response.data)
.then((data) => {
// do stuff
return data; // need to set this to state back in component class
})
.catch(error => {
console.log(error);
});
}
}
If you return this axios call, it's itself a promise and you're not waiting until it resolves, so there's no need to use async.

How do I update an object state in react via hooks

This is a simple question. How do I successfully update state object via react hooks?
I just started using hooks, and I like how it allows to use the simple and pure JavaScript function to create and manage state with the useState() function, and also, make changes that affect components using the useEffect() function, but I can't seem to make update to the state work!
After making a request to an API, it return the data needed, but when I try to update the state for an error in request and for a successful request, it does not update the state. I logged it to the browser console, but no change was made to the state, it returns undefined.
I know that I'm not doing something right in the code.
Here is my App component, Its a single component for fetching and updating:
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
export default function App() {
// Set date state
const [data,setData] = useState({
data: [],
loaded: false,
placeholder: 'Loading'
});
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
SetData({placeholder: 'Something went wrong'});
}
response.json()
})
.then(result => {
console.log(data);
setData({data: result});
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
There are a few things you can improve:
the react-hook useState does not behave like the class counterpart. It does not automatically merge the provided object with the state, you have to do that yourself.
I would recommend if you can work without an object as your state to do so as this can reduce the amount of re-renders by a significant amount and makes it easier to change the shape of the state afterwards as you can just add or remove variables and see all the usages immediately.
With a state object
export default function App() {
// Set date state
const [data,setData] = useState({
data: [],
loaded: false,
placeholder: 'Loading'
});
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText); // Goto catch block
}
return response.json(); // <<- Return the JSON Object
})
.then(result => {
console.log(data);
setData(oldState => ({ ...oldState, data: result})); // <<- Merge previous state with new data
})
.catch(error => { // Use .catch() to catch exceptions. Either in the request or any of your .then() blocks
console.error(error); // Log the error object in the console.
const errorMessage = 'Something went wrong';
setData(oldState=> ({ ...oldState, placeholder: errorMessage }));
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
Without a state object
export default function App() {
const [data, setData] = useState([]);
const [loaded, setLoaded] = useState(false);
const [placeholder, setPlaceholder] = useState('Loading');
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText); // Goto catch block
}
return response.json(); // <<- Return the JSON Object
})
.then(result => {
console.log(data);
setData(data);
})
.catch(error => { // Use .catch() to catch exceptions. Either in the request or any of your .then() blocks
console.error(error); // Log the error object in the console.
const errorMessage = 'Something went wrong';
setPlaceholder(errorMessage);
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
The correct way to update an Object with hooks it to use function syntax for setState callback:
setData(prevState => {...prevState, placeholder: 'Something went wrong'})
Following method will override your previous object state:
setData({placeholder: 'Something went wrong'}); // <== incorrect
Your final code should look like this:
.then(response => {
if (response.status !== 200) {
setData(prevObj => {...prevObj, placeholder: 'Something went wrong'});
}
return response.json()
})
.then(result => {
setData(prevObj => {...prevObj, data: result});
});

Errror fetching data with promise

I am new with promise and I can not to solve an issue with promise.
I have to return a new state in function loadPosts after fetching data from API:
[loadPosts]: (state, index) => {
fetchPosts().then( data => {
return {
...state,
postState : {
postList : data.data
}
}
})
}
And this is my fetchPosts function:
export const fetchPosts = () => {
console.log("Fetch posts...");
fetch(process.env.REACT_APP_API_URL + '/post')
.then(response => response.json())
.then(data => {
return data
})
.catch(error => console.error(error))
}
I get "TypeError: Cannot read property 'then' of undefined"
In my understanding, first and second then of fetchPosts function, should return a promise with resolved value but instead I get undefined.
If I change fetch post in this way (adding return):
export const fetchPosts = () => {
console.log("Fetch posts...");
return fetch(process.env.REACT_APP_API_URL + '/post')
.then(response => response.json())
.then(data => {
return data
})
.catch(error => console.error(error))
}
I get another error: reducer "app" returned undefined. To ignore an action, you must explicitly return the previous state.
How can I use promise to reach my goal?
Thanks
First, lets fix your fetchPosts function
export const fetchPosts = () => {
console.log("Fetch posts...");
return fetch(process.env.REACT_APP_API_URL + '/post')
.then(response => response.json())
// the following code is not needed at all
//.then(data => {
// return data
// })
// I prefere not to do the error handling here,
// instead let the caller handle the error
.catch(error => console.error(error))
}
Now that the fetch posts function actually returns something, I can only tell you that there is no way from inside the function in your first code snippet to return a new state with the posts that the fetchPosts promise resolves to.
It looks a lot like a reducer though, so I recommend you take a look at redux-thunk that allows you to enhance redux with a middleware for async behavior and you can then dispatch functions to the store that returns promises.
1.) You need to return the fetch() so that you can chain a .then().
2.) You need to have a default case in your reducer which returns the state.

Categories