I have a redux action that returns a promise, so that the ui can respond accordingly based upon the result. This action sends a request to the backend, and when it gets a successful response from the server, it resolves.
redux action:
export const preauthorizePayment = (amount, connectedAccountId, customerId) => {
console.log("action reached")
return (dispatch) => {
dispatch({ type: types.UPDATING })
return new Promise((reject, resolve) => {
console.log("promise reached")
axios.get(PRE_AUTH_ENDPOINT, {
params: {
amount: amount,
customerId: customerId,
connectedAccountId: connectedAccountId
}
})
.then(res => {
console.log("then reached...")
if (res.data.result == 'success') {
dispatch({ type: types.UPDATE_SUCCESS });
console.log("if reached...");
return resolve();
} else {
console.log("else reached...")
console.log(JSON.stringify(res))
dispatch({
type: types.UPDATE_FAIL,
info: res.data.errorInfo,
errorCode: res.data.errorCode
})
return reject()
}
})
.catch(err => {
dispatch({ type: types.UPDATE_FAIL, errorInfo: err })
return reject()
})
})
}
}
the UI piece looks like:
props.preauthorizePayment(amount, connectedAccountId, customerId)
.then(() => {
console.log("pre-auth then reached")
// pre-auth success
setCheckInModal(false)
props.checkIn(props.evnt.id, props.role)
})
.catch(err => setCheckInModal(true))
What im experiencing is that everything is working properly up until the resolve() portion of the redux action. din the if block of the redux action's then(), the dispatch gets fired, the log fires, but it doesn't resolve. The UI piece's then() does not execute, no log, nothing. initially I didnt have the return keyword in the resolve line, and once I added it, it worked once. but then it stopped.
Whats going on?
Related
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
I got this error when using promise:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
I'm pretty sure this error is got from my promise async request, when i not using promise it working fine.
But when im using promise to handle async request, i got this error, i need promise to handle it, so how can i fix this?
This is my Async Request.
export const LoginAPI = (username, password) => (dispatch) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({ username, password });
const promise = new Promise((resolve, reject) => {
axios.post('http://localhost:8000/api/accounts/login/', body, config)
.then((response) => {
dispatch({ type: LOGIN_SUCCESS, payload: response.data });
SwalAlert('Successfully Login...', 'Welcome To 7Blankpages...', 'success');
resolve(true);
})
.catch((err) => {
dispatch({ type: LOGIN_FAILED });
if (err.response.data.non_field_errors) SwalAlert('Oops...', `${err.response.data.non_field_errors}`, 'error');
reject(false);
});
});
return promise;
};
This is my Login Handling
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
const response = await LoginUser(username, password).catch(() => setIsLoading(false));
if (response) {
setIsLoading(false);
setUsername('');
setPassword('');
}
};
if (auth.isAuthenticated && auth.user.is_active) {
return <Redirect to="/" />;
}
This happened because your handlesubmit function is asynchronous that makes if block to get executed redirecting to url '/' changing the component. but the function setisloading,setusername etc in your handlesubmit will still be excecuting which led to memory leak. To prevent this add useEffect hook with cleanup function.
useEffect(()=>{
//cleanup function
return ()=>{
setisloading(false);
setusername('');
setpassword('');
}
})
I'm trying to add an async/await in my code to have the app wait for the execution of a function to invoke an other one, but I can't seem to be able to figure out where my code is wrong.
I have an API call in a redux action, this is the action
export const editSecondaryCategory = (text) => {
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
I want, in my component, to wait after this action is completed to call an other function to update the state. Ideally, it should be something like this (I guess?):
async updateCategory() {
// do the thing I have to wait for
const propsUpdate = await this.props.editSecondaryCategory(text);
// if you've done the thing then call the function to update the state
if (updatedCat) {
this.updateStateArray();
}
}
And I'd call this.updateCategory() inside my component after the user is done editing the information.
Clearly, this code does not work. I know it's wrong, I just don't know why. Basically I have no clue what to write inside updateCategory() to make this work.
Please help lol
You need to rewrite editSecondaryCategory function to make it async.
export async function editSecondaryCategory(text){
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
Currently, your function is not an async function do the above changes and check.
I have a Vue-App which runs with Vuex and Axios. In this app I have vuex-store which handles API-calls, but a problem is that when I call the store-actions I cant chain the response in the caller.Any ideas what Im doing wrong?
Calling code:
import { FETCH_PRODUCTS, ADD_PRODUCT } from './actions.type'
methods: {
sendNewProduct () {
this.$store
.dispatch(ADD_PRODUCT, this.newProductForm)
.then(() => {
console.log('This never gets called')
})
}
}
Vuex-store:
const actions = {
[ADD_PRODUCT] (context, credentials) {
return new Promise((resolve) => {
ApiService
.post('/Products/', {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store
.dispatch(FETCH_PRODUCTS)
resolve(data)
})
.catch(({ response }) => {
console.log(response)
context.commit(SET_ERROR, 'Error adding product')
})
})
}
}
const actions = {
[ADD_PRODUCT](context, credentials) {
return ApiService.post("/Products/", {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store.dispatch(FETCH_PRODUCTS);
return data;
})
.catch(({ response }) => {
console.log(response);
context.commit(SET_ERROR, "Error adding product");
throw new Error("Error adding product");
});
}
};
I've removed the new Promise(...) because axios already creates a promise.
If added a return data in the then callback and a throw in the catch callback to let the calling api receive the data/error.
Note that the promise resolves before the FETCH_PRODUCTS completes, to make sure that action is also completed, you'd write:
.then(({ data }) => {
return this.$store.dispatch(FETCH_PRODUCTS)
.then(() => data);
})
I am trying to make a GET request for some data.
Here is my action call.
componentDidMount() {
this.props.fetchData(() => {
this.setState({ isLoading: false });
});
}
Prior to completion I'd like to display "Loading..." momentarily as the fetch request is making it's trip. I'm using a callback for this and setting my local state.
Here is my action creator with a 'callback'.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then(() => callback())
.catch((err) => {
console.log(err.message);
});
}
}
And here is that same function above but dispatching the action so that I can receive as props and render to my ui.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => {
console.log(err.message);
});
}
}
My question is how do you make the callback and dispatch the action in the same action creator function? Is that even good practice?
You could do something like this
componentDidMount() {
this.setState({ isLoading: true }, () => {
// ensuring that you make the API request only
// after the local state `isLoading` is set to `true`
this.props.fetchData().then(() => this.setState({ isLoading: false });
});
}
and, fetchData would be defined as follows
export function fetchData(callback) {
return (dispatch) => {
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => console.log(err.message));
}
}
If you're using the redux-thunk middleware to use asynchronous actions, then these actions will return Promises; so you can set your component's local state after that Promise resolves.
In the component:
.....
componentDidMount() {
this.props.fetchData();
}
....
export default connect((state) => ({loading: state.loading, data: state.data}))(Component);
In the actions, you should do :
....
export function fetchData() {
return (dispatch) => {
dispatch({ type: FETCHING_DATA}); //dispatch an action for loading state to set it to true
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: DATA_FETCHED, payload: response }))
.catch((err) => console.log(err.message));
}
}
....
In the reducer, you should do :
....
case 'FETCHING_DATA':
return {
...state,
loading: true,
}
case 'DATA_FETCHED':
return {
...state,
data: action.payload,
loading: false,
}
....
I personally feel that you shouldn't put any business logic in your component because it can cause some problems later when you want to refactor your app. This means that there shouldn't be any .then in your component and everything should be guided through redux (if there is some side effects in your app). So, you should control your loading state from redux itself and not inside the component.