I try to update some data in my mounted part, in my Vue.js component. Here is my code :
methods: {
initData() {
const self = this;
axios
.get("/checkAuthentification")
.then((response) => {
(self.userFields.userAuthenticated = response.data),
console.log("method"),
console.log(self.userFields.userAuthenticated),
console.log("reponse"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
console.log("between");
console.log(self.userFields.userAuthenticated);
if (self.userFields.userAuthenticated == "true") {
axios
.get("/getAuthenticatedUserData")
.then((response) => {
(self.userId = String(response.data.id)),
(self.userFields = response.data),
console.log("user data"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
}
},
},
mounted() {
this.initData();
},
In the first axios call, it works perfectly, the two console log give the expected value "true". But on the next console.log after between, it says 'undefined'. Thue userFields doesn't update after the firt axios call. I don't understand because i did the same thing on a submit button on a form, and it worked perfectly.
I ask here because I looked other posts and the answer is always to use const=this, which I did.
Thank you for your help !
To answer your questions first you would have to understand the nature of asynchronous operations in Javascript. I will try my best to explain but will leave this link in case you would like to read more about it
On your example you have:
axios.get('/checkAuthentification').then(response =>{
console.log('CHecked authentication')
...
})
console.log('between');
console.log(self.userFields.userAuthenticated);
When you do the axios.get what happens is that an asynchornous operation will be called (asynchornous = won't wait for the answer for the code to continue execution). You will contact your backend server and ONLY when you receive an answer the console.log('checked authentication') will run but in the meantime (remember axios.get is asynchronous) the console.log('between'); AND console.log(self.userFields.userAuthenticated); will execute before you receive an answer from the backend server.
How can we solve this
We can solve this with two approaches. The first one would be the more old approach - waiting for promises to resolve by the then keyword
axios
.get('/checkAuthentification')
.then(response =>{
console.log('CHecked authentication')
console.log('between');
console.log(self.userFields.userAuthenticated);
return response;
})
.then(() => {
// do more operations after promise was resolved
})
Or we can do a more modern way - we can use async/await.
async initData() {
const response = await axios.get('/checkAuthentification');
console.log('CHecked authentication')
console.log('between');
console.log(self.userFields.userAuthenticated);
}
state: {
userFields: {
userAuthenticated: false,
...
}
},
methods: {
initData() {
const self = this;
axios
.get("/checkAuthentification")
.then((response) => {
(self.userFields.userAuthenticated = response.data),
console.log("method"),
console.log(self.userFields.userAuthenticated),
console.log("reponse"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
},
getUserData() {
if (this.userFields.userAuthenticated) {
axios
.get("/getAuthenticatedUserData")
.then((response) => {
(self.userId = String(response.data.id)),
(self.userFields = response.data),
console.log("user data"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
}
}
},
watch: {
'userFields.userAuthenticated': function() {
this.getUserData()
}
},
mounted() {
this.initData();
},
Related
Hello stackOverflow friends. I'm starting to use redux toolkit, and there is some stuff that I don't get.
I have this Slice:
const envSlice = createSlice({
name: "env",
initialState: { envs: [], loading: false, error: false },
reducers: {},
extraReducers: {
[fetchEnv.pending]: (state) => {
state.loading = true;
},
[fetchEnv.fulfilled]: (state, action) => {
state.envs = action.payload;
state.loading = false;
},
[fetchEnv.rejected]: (state) => {
state.loading = false;
state.error = true;
},
},
});
export default envSlice.reducer;
And I'm trying to fetch some data from a mongo server. this code used to work in the regular redux:
--- fetching all the envs ---
export const fetchEnv = createAsyncThunk("admin/fetchEnv", (thunkAPI) => {
axios
.get("http://10.0.0.6:5000/admin/getAllEnv")
.then((response) => response.data)
.catch((error) => {
console.log(error);
return thunkAPI.RejectWithValue(error);
});
});
But for some reason its always goes into the catch and rejects - even when this is a response.
this code works:
export const fetchEnv = createAsyncThunk(
"admin/fetchEnv",
async (_, thunkAPI) => {
try {
const response = await axios.get("http://10.0.0.6:5000/admin/getAllEnv");
return response.data;
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
}
);
My problem is that I made this work, but I cant understand why the first createAsyncThunk doesn't work, and why the second is working.
To me both functions look kind of the same.
This is an example from RTK docs (for some reason they dont cover error handling in this example, I didn't found one with):
thanks (: .
From the documentation:
and a callback function that should return a promise
Your async thunk that doesn't work isn't returning anything:
export const fetchEnv = createAsyncThunk("admin/fetchEnv", (thunkAPI) => {
return axios //<----return a promise
.get("http://10.0.0.6:5000/admin/getAllEnv")
.then((response) => response.data)
.catch((error) => {
console.log(error);
return thunkAPI.RejectWithValue(error);
});
});
Or you can have a bodiless arrow function:
export const fetchEnv = createAsyncThunk(
'admin/fetchEnv',
(
thunkAPI
) => //no { or }
axios
.get('http://10.0.0.6:5000/admin/getAllEnv')
.then((response) => response.data)
.catch((error) => {
console.log(error);
return thunkAPI.RejectWithValue(error);
})
);
so I'm using a popup to log my users in with firebase:
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then(async (result) => {
if (result.additionalUserInfo.isNewUser) {
// problem is this line
await setNewUserInformation(result.user.uid)
}
const { user } = result
setUser(user)
// and this line
window.location.href = 'newRoute'
})
.catch((error) => {
console.log('ERROR:', error)
})
}
so if I remove window.location.href = 'visited' this all works fine and it sets in firebase. I'm probably doing something stupid but I cant figure out how to wait for this function to fire setNewUserInformation and to complete before I move to the new page?
function code:
export const setNewUserInformation = (userId) => {
return {
type: 'SET_NEW_USER_INFORMATION',
userId,
}
}
this then has a redux observable epic listening to it:
return action$.pipe(
ofType('SET_NEW_USER_INFORMATION'),
mergeMap((action) => {
return from(
firebaseApp.database().ref(firebaseRef).update(userInformation),
).pipe(
mergeMap(() => {
return [updatedUserInformationSuccess()]
}),
catchError((error) => of(updatedUserInformationFailure(error))),
)
}),
)
setNewUserInformation() is an action creator, which is sync. You do not need to wait for it as it does not return anything useful to you logic. What you need to do, is move window.location.href = 'newRoute' to separate logic, and make it depend on state returned from action creators updatedUserInformationSuccess() and updatedUserInformationFailure(error). If your component is functional, put this logic in a useEffect. If it is a class component, use ComponentDidUpdate lifecycle method.
Use it like below
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then(async (result) => {
new Promise((resolve, reject) => {
if (result.additionalUserInfo.isNewUser) {
// problem is this line
setNewUserInformation(result.user.uid)
}
const { user } = result
resolve(user)
}).then((user)=>{
setUser(user)
// and this line
window.location.href = 'newRoute'
})
})
.catch((error) => {
console.log('ERROR:', error)
})
}
Because on then You can returned a Promise and resolve later. We could re-write the code above like this below:
const loginToApp = (provider) => {
firebaseApp
.auth()
.signInWithPopup(provider)
.then((result) => {
if (result.additionalUserInfo.isNewUser) {
// return for next resolve function
return setNewUserInformation(result.user.uid).then(() => result);
}
return result;
})
.then((result) => {
// after all above promises resolve
const { user } = result
setUser(user)
// and this line
window.location.href = 'newRoute'
})
.catch((error) => {
console.log('ERROR:', error)
})
}
Are you using React?
If yes, then you can simply use didUpdate Cycle to route to new url after successful action dispatched. Move your "window.location.href = 'newRoute'" under the ComponentDidUpdate with props check.
Is there a way in React JS to block a request if it is already doing it, i am talking of the same request.
EDIT:
This is my code:
const fetching = false;
export default (type, filter, dateFilter, position) => {
if(fetching) return Promise.reject(new Error('Request in progress'));
fetching = true;
return fetch(URL + `/search/${type}/${filter}/${dateFilter}/${position}/0/0`)
.then(response => Promise.all([response, response.json()]))
//!!! My problem is that now i cannot put .then(() => fetching = false))
// here.If i put it i get undefined in my then(res) of my getDataApi
// so i cannot make requests again because fetching stays in true.
}
for better understanding this is my console with:
.then(() => {
fetching = false;
console.log("fetching", fetching)
})
and without:
actions.js
export const fetchData = (type, filter, dateFilter, position) => {
return (dispatch, getState) => {
const state = getState();
dispatch(getData())
getDataApi(type, filter, dateFilter, position)
.then(res => {
console.log("RES", res)
if (res !== undefined) {
console.log("entro")
//here it doesnt enter if i put fething false above
// is like somehow the promise.all is not resolved if i
// put it above or under the then with the fetching =
// false but i need it, what can i do?
if (state.dataReducer.data.length === 0) {
dispatch(getDataSuccess(res[1]))
} else {
dispatch(getDataSuccess(res[1], state.dataReducer.data))
}
}
})
.catch((err) => console.log(9999, err))
}
}
Not sure you really need to over complicate this, hold some state that indicates your request is already in progress so subsequent requests can be ignored.
You don't mention how you are managing your app state so here's a very simple example based on your code
let fetching = false;
export default (type, filter, dateFilter, position) => {
if (fetching) return Promise.resolve();
fetching = true;
return fetch('...')
.then(response => {
// handle response
})
.catch(e => {
// handle error
})
.then(() => {
fetching = false; // reset state
});
}
I solved it.This is my code:
return fetch(URL + `/search/${type}/${filter}/${dateFilter}/${position}/0/0`)
.then(response => Promise.all([response, response.json()]))
.then(([response, responseObj]) => {
fetching = false;
return [response, responseObj];
})
.catch(err => {
fetching = false;
return Promise.reject(err); // If you want to handle the error in a chained .catch()
})
I'm brand new to react native and I've been browsing through snippets of code and am confused as to how promises are passed along.
I have this event handler onRefresh() that is called when I pull down on a flatlist and I'm trying to have it use the return of apiSearchDB when it returns true/false.
onRefresh = () => {
this.setState({...}, () => {
return this.apiSearchDB()
.then(function(response) {
console.log(response);
})
.catch((error) => {
console.log(error);
});
})
}
apiSearchDB = () => {
return fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => {
this.setState({
...
}, () => {return true})
return true;
}).catch((error) => {
console.error(error);
return false;
})
}
The line console.log(response); only prints undefined and I can't figure out why.
Could my handler also be written as
onSearch = () => {
return new Promise((resolve, reject) => {
var response = this.apiSearchDB();
response
? resolve();
: reject();
}
});
}
or onSearch = () => {...} and function onSearch(){...}?
Thank you in advance!
You should read more about using promises (good article - We have a problem with promises). However, two basic rules that will help you in this case are are:
The value returned from a promise is wrapped in a promise.
Promises can be chained.
The apiSearchDB should return a promise that contains the json as the resolved value, and error as the rejected value:
apiSearchDB = () =>
fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => json)
// can be removed unless you want to do something with the error before passing it on
.catch((error) => Promise.reject(error));
The onRefresh (or onSearch) method should get the promise from apiSearchDB, and add it's own chain. Resolve promise should be handled with the then handler. If it's the rejected value, it will be handled by the catch handler:
onRefresh = () =>
this.apiSearchDB()
.then((response) => {
console.log(response);
// do something with response
this.setState({...}, () => {
});
return response;
})
.catch((error) => {
console.log(error);
// do something with error
this.setState({...}, () => {
});
});
}
I'm calling a function inside a then statement, and that function has to wait for an event to fire, but my initial function is returning undefined almost immediately:
// call.js
const dialogflow = require('./dialogflow')
module.exports = {
receive: functions.https.onRequest((request, response) => {
...
let respondToUser = getUserId
.then((uid) => {
payload.uid = uid
dialogflow.handleIncoming(payload).then((result) => {
console.log(result)
})
})
.then((result) => {
console.log(result)
response.end()
})
...
}
}
// dialogflow.js
module.exports = {
handleIncoming: (payload) => {
...
let df = dialogflow.textRequest(message.message, {
sessionId: payload.from
})
.on('response', (response) => {
return response.result.fulfillment.speech
})
.on('error', (error) => {
return 'That\'s an error on my end. Try again later!'
})
.end()
}
}
The goal is to call dialogflow.handleIncoming(payload) from call.js, wait for it to return some text, and then continue. But no matter how I have structured it, receive just keeps blowing through it and dialogflow.handleIncoming(payload) ends up undefined.
I've tried using a promise on df with no success, and I can't figure out how to make respondToUser wait for a full response from handleIncoming. Everything else is working so I'm only including relevant code.
This is using api.ai (dialogflow), but in cloud functions in Firebase if that helps. Appreciate any help!
Problem is dialogflow.handleIncoming(payload) is not structured for async. Try this:
// dialogflow.js
exports.handleIncoming = (payload) =>
new Promise((resolve, reject) => {
...
let df = dialogflow.textRequest(message.message, {
sessionId: payload.from
})
.on('response', (response) => {
resolve(response.result.fulfillment.speech)
})
.on('error', (error) => {
reject ('That\'s an error on my end. Try again later!')
})
.end()
}
Your receive function isn't waiting for dialogflow.handleIncoming(payload) to complete. The then function that contains it doesn't have a return statement, so it's returning undefined rather than returning the result of dialogflow.handleIncoming (which is what you want).
let respondToUser = getUserId
.then((uid) => {
payload.uid = uid
return dialogflow.handleIncoming(payload)
})
.then((result) => {
console.log(result)
response.end()
})
The next then statement will contain the response from diagflow.handleIncoming.