How to call action from action on Axios response Vuex? - javascript

I am trying to implement the following logic: call login then if response is ok, call method for retrieving user data.
Login action
loginUser({commit,dispatch}, credentials) {
const form = new URLSearchParams();
form.append("login", credentials.login);
form.append("password", credentials.password);
const formConfig = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
return Axios.post(loginUrl, form, formConfig).then(
(response) => {
commit('setErrorMessage', '', {root: true});
commit('setAuthenticated', response.headers[authorization]);
dispatch('getUserByLoginAuth',credentials.login);
},
(error) => {
if (error.response.status===500){
commit('setErrorMessage', error.response.data.message, {root: true});
} else {
commit('setErrorMessage', error.response.data, {root: true});
}
});
},
The second action dispatched from the one above:
getUserByLoginAuth({commit, getters}, login) {
return getters.authenticatedAxios.get(userUrl + '/find', {
params: {
login: login
}
}).then(
(response) => {
commit('setErrorMessage', '', {root: true});
commit('setUser', response.data);
},
(error) => {
commit('setErrorMessage', error.response.data, {root: true});
});
},
This action is called from the second time only(as I understand it is related to promise).
Here is a code from component which dispatches login action
this.$store.dispatch('loginUser', this.credentials).then(() => {
this.errorMessage = this.getError;
if (this.errorMessage.length) {
this.errorOccurred = true;
}
this.$router.push({path: '/user/' + this.getId});
});
this.errorOccurred = false;
},
Here also I am not sure if I am doing routing in correct place. As I understand then will work with promise from getUser so errorMessage from login might be lost. I would like to prevent it and make dispatch of getUser correctly from the first time

I don't entirely follow what you're asking but this seems likely to be a problem:
dispatch('getUserByLoginAuth',credentials.login);
The problem isn't the call itself. The problem is that it's kicking off a new asynchronous action without chaining it onto the existing promises. From the perspective of loginUser everything is done, it won't wait for getUserByLoginAuth.
The result will be that the then in your component will be called before getUserByLoginAuth is done. I would imagine this is why it seems to work the second time, because it's picking up the relevant data from the previous call.
The solution would be simply to change it to:
return dispatch('getUserByLoginAuth',credentials.login);
By putting in a return it adds it to the promise chain, so loginUser won't be treated as complete until getUserByLoginAuth is done.

Related

Proper way to handle a page refresh based on a redux request change

I have created a redux that is going to request an API and if the result is 200, I want to redirect the user to another page using history.
The problem is: I don't know how to trigger this change if the action is a success.
I could redirect the user in my useCase function but I can't use history.push pathName/state argument because it only works in a React component.
So this is what I have done in my React component:
const acceptProposalHandler = () => {
store.dispatch(acceptProposal(id)).then(() => {
setTimeout(() => {
if (isAccepted) { //isAccepted is false by default but is changed to true if the
//request is 200
history.push({
pathname: urls.proposal,
state: {
starterTab: formatMessage({id: 'proposalList.tabs.negotiation'}),
},
});
}
}, 3000);
});
};
Sometimes it works but other times it wont. For some reason, .then is called even if the request fails.
I'm using setTimeOut because if I don't, it will just skip the if statement because the redux hasn't updated the state with isAccepted yet.
This is my useCase function from redux:
export const acceptProposal = (id: string) => async (
dispatch: Dispatch<any>,
getState: () => RootState,
) => {
const {auth} = getState();
const data = {
proposalId: id,
};
dispatch(actions.acceptProposal());
try {
await API.put(`/propostas/change-proposal-status/`, data, {
headers: {
version: 'v1',
'Content-Type': 'application/json',
},
});
dispatch(actions.acceptProposalSuccess());
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
}
};
What I'm doing wrong? I'm using Redux with thunk but I'm not familiar with it.
".then is called even if the request fails." <- this is because acceptProposal is catching the API error and not re-throwing it. If an async function does not throw an error, it will resolve (i.e. call the .then). It can re-throw the error so callers will see an error:
export const acceptProposal = (id: string) => async (
// ... other code hidden
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
// ADD: re-throw the error so the caller can use `.catch` or `try/catch`
throw error;
}
};

Axios cancel Request shows browser alert

Axios cancel Request shows browser alert which stops the execution until you click ok.
I want to cancel my requests, all my Api calls are in separate file named apiCalls.js.
Component with cancelToken.
componentDidMount() {
const CancelToken = axios.CancelToken;
// create the source
this.source = CancelToken;
}
persistCancel = (cancel) => {
this.setState({cancels: [...this.state.cancels, cancel]})
}
componentWillUnmount(){
this.state.cancels.forEach((c) => c());
}
this is my Api call from component.
getScoreCardCall({profileId, campaignIds, startDate, endDate}, (scoreCards) => {
//success
this.setState({
scoreCards,
showComcardsLoader: false
})
},this.source,this.persistCancel);
and in the apiCalls.js
export function getScoreCardCall(params,callback, source,onRequest){
axios.get(url,
{
cancelToken: new source(function executor(c) {
onRequest(c);
}),
params:{
profileId: params.profileId,
campaignId: params.campaignIds.toString(),
startDate: params.startDate,
endDate: params.endDate,
}
})
.then(res => {
if(callback != null){
if(res.data.length!=0){
callback(res.data);
}
}
})
.catch(err => {
if (axios.isCancel(err)) {
console.log(err.message);
}
})
}
Can someone please tell me why is alert showing with every request cancellation?? or what i am doing wrong?
axios#cancellationdescribes two ways to use the cancelToken. You used the first way, with source.token/source.cancel. I started out that way, and had a similar problem as yours: Once a request to a particular URL was canceled, I could never get a successful response from that URL again. I switched to the second method using an executor function and the problem went away. I guess was sharing the same cancelToken for multiple requests, which is what they say you can do with the executor function method. Anyway, maybe that would work for you too.

How to handle AJAX response with service worker (Workbox) when connection is restored

I'm building a PWA using the Vue CLI 3 PWA plugin, and I'm not sure how to handle the response from an AJAX call made while offline.
All I have is basically sample code from the Workbox documentation...
// service-worker.js
const queue = new workbox.backgroundSync.Queue('CR_OfflineQueue')
self.addEventListener('fetch', (event) => {
const promiseChain = fetch(event.request.clone())
.catch((err) => {
console.log('Queued Event:', event.request)
return queue.addRequest(event.request)
})
event.waitUntil(promiseChain)
})
The AJAX is just a basic Axios get() that's called when the store is loaded and in a test mutation
// store.js
getUsername () {
return axios.get(
'http://localhost:8005/username/',
{
responseType: 'json',
withCredentials: true
})
}
const store = new Vuex.Store({
state: {
username: null
},
mutations: {
test_api (state) {
getUsername()
.then((res) => {
state.username = res.username
})
.catch((err) => {
console.error('getUsername:', err)
})
}
}
})
The request is being successfully replayed, but I have no idea what to do with it, and I'm unable to find an example using workbox.backgroundSync.Queue.
As far as getUsername() is concerned, the request failed, but how do I have the calling function essentially pick up where it left off?
I don't think that need something to check the queue for the request at a regular interval to "manually" re-trigger (not via the auto replay). How would I use the queue class to take the result from the replayed request and set the username property?

How to get data from axios call in redux action? React JS search project

I have a search of weather for some cities. I would like to create info modal when a user tries to find a city that is not in the base. In this case I receive 404 error from my API.
I fetch the data every time when user click on search button. I use axios to do it and whole project is based on React and Redux. Everything is clear for me but I have a problem with pass valid response to payload.
How should I do it? In an another file and use react component lifecycle?
action.js
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
axios.get(url)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return {
type: FETCH_WEATHER,
payload: request
};
}
In your example the return will be called before Axios completes it's API call, because it's asynchronous. One solution to this is to put the return inside the .then like this:
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
axios.get(url)
.then(function (response) {
// won't get called until the API call completes
console.log(response);
return {
type: FETCH_WEATHER,
payload: response.data
};
})
.catch(function (error) {
// won't get called until the API call fails
console.log(error);
return {
type: FETCH_WEATHER_ERROR,
payload: error
};
});
}
You should also return an error in the catch if the API call is unsuccessful.
In your snippet, request will always be undefined because axios.get is an async operation and return happens before axios.get finishes execution. You do something like this:
export async function fetchWeather(city) {
try {
const request = await axios.get(`${ROOT_URL}&q=${city}`);
// server returned a 2XX response (success)
return {
type: FETCH_WEATHER,
payload: request
};
} catch(error) {
// Network failure or 4XX or 5XX response.
return {
type: FETCH_WEATHER_FAIL
payload: error
}
}
}

How to return value in VueJS Mixin

I'm currently developing a webapp in Vuejs. I created a Mixin that I can access globally which handles any request to my api:
export default {
data() {
return {
apiURL: 'http://example.com/api',
timeout: 10000,
};
},
methods: {
callAPI(method, url, body) {
this.$http({
url: this.apiURL + url,
method,
body,
timeout: this.timeout,
})
.then((response) =>
response,
(response) => {
if (response.data.error) {
this.error = response.data.error;
} else {
this.error = 'We can\'t connect to the server. Please try again in a few minutes.';
}
return response;
});
// return 'test';
},
},
};
Now, in some of my components I call the api function:
const api_response = this.callAPI('POST', '/auth', credentials);
alert (api_response);
It works fine, but one thing doesn't work as expected. I expect my api_response constant to have the value of response but it is always undefined. So every time I got this alert with undefined. How is that possible? When I uncomment the return 'test' line it works: I got an alert with test, but it doesn't seem to work within the this.$http part...
Your callAPI has no return statement, so it returns undefined. If it returned your $http call, it still would not give you response, but would be a Promise, so you would want to do something like
let api_response;
this.callAPI('POST', '/auth', credentials).then((response) => api_response = response);

Categories