Async Redux Action: add auth token to fetch() request - javascript

I currently have an async redux action (w/ Middleware) which fetches data from my backend. I am adding Firebase authentication, and want to include the auth token to this request. The auth token is fetched asynchronously using the function getAuthToken() below. The below code doesn't work, complaining
Cannot read property 'resolve' of undefined
This simple task, chaining the output of one async to another, but I'm new to JS. How can I accomplish this?
// I'm sure returning from an async function like this is wrong.
function getAuthToken() {
firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then(idToken => {
return idToken;
});
}
// Async redux action.
export function getData(userId) {
return dispatch => {
return fetch(`${BASE_URL}/data/${userId}`, {
headers: { Authorization: getAuthToken().resolve() } // Can I even resolve like this?
})
.then(response => response.json())
.then(json => dispatch(returnSurveys(json)));
};
}

Short-term solution: first await for token then send your request.
function getAuthToken() {
return firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then(idToken => {
return idToken;
});
}
export function getData(userId) {
return dispatch => {
return getAuthToken().then(token =>
fetch(`${BASE_URL}/data/${userId}`, {
headers: { Authorization: token } // Can I even resolve like this?
})
.then(response => response.json())
.then(json => dispatch(returnSurveys(json))));
};
}
Downside: calling getAuthToken each time you want to ask backend for something is definitely bad practice.
So you better put token into Redux as well as other data is.
And then you may either pass token from outside:
export function getData(userId, token) { ....
Or, probably better, ask store for token right in your thunk(second parameter getState is passed by redux-thunk):
export function getData(userId) {
return (dispatch, getState) => {
return fetch(`${BASE_URL}/data/${userId}`, {
headers: { Authorization: getState().path.to.token.in.your.store }
})
For some period it assumed to be rather bad approach, that's why it's not widely known. But there are different points of view and to me your case suiting that very well.

Related

How can I write a .then function with async/await, so that I can catch the response from axios (in seperate files and methods, in vue)

My target is to catch a respone, from a axios request, which works greate with .then, but I would like use async/await, since it is a new approach with lots of benefits.
(The update method is called multiple times)
How transform my saveEdit method (which gets a response form the update method) with async/await, so that I can catch the response from axios.
Method of my .vue file:
...
saveEdit (event, targetProperty, updateValue) {
this.update(this[updateValue])
.then((result) => {
if (result.status === 200) {
this.fetchData()
this.cancelEdit()
}
})
}
...
My function of my store module:
(api is a handler for axios, basicall axios. ...)
update ({ commit, rootGetters }, details) {
...
const requestUrl = `some adress`
return api
.patch(
requestUrl,
validatedDetails
)
.then(response => {
return response
})
.catch(error => {
return Promise.reject(error)
})
}
Other stackoverflow posts related to that problem, did answer my question, since in the examples are in one file and one method.
Thanks in advance
you can try someting like:
update ({ commit, rootGetters }, details) {
...
const requestUrl = `some adress`
return api.patch(
requestUrl,
validatedDetails
)
}
and :
async saveEdit (event, targetProperty, updateValue) {
try {
const result = await this.update(this[updateValue])
if (result.status === 200) {
this.fetchData()
this.cancelEdit()
}
} catch (error) {
// handle api call error
}
}

Wait for axios API call in vuex to finish before continuing vue-router guard

I have a django-rest-axios-vuejs application stack for which I'm trying to do something in the vue-router.
In the vue-router beforeEach guard, I'm verifying permissions and it is done by checking something in an object called me in vuex store. Everything works well except when I refresh the page.
Indeed refreshing the page also clears vuex store and my beforeEach tries to check the me object from the store which is empty.
Therefore I'd like to fetch this me object from the API if it isn't in the store.
The problem is that it takes "some time" and the hasPermission() method executes before the API call is finished.
So I tried to put a await keyword before my API call but it doesn't work.
My beforeEach guard :
router.beforeEach(async (to, from, next) => {
const isLoggedIn = getIsLoggedIn()
handleLoggedInStatus(isLoggedIn)
if (to.meta.requiresAuth) {
if (isLoggedIn) {
if (to.meta.permission) {
if (!store.state.me) await store.dispatch('FETCH_ME')
hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
} else {
next()
}
} else {
next({ name: 'LoginForm' })
}
} else {
next()
}
})
My action from the store :
actions: {
FETCH_ME: (state) => {
http
.get('base/users/me/')
.then(response => {
state.me = response.data
})
.catch(error => {
console.log(error)
})
}
}
The only way I've found to make it wait is to do the following :
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
router.beforeEach(async (to, from, next) => {
const isLoggedIn = getIsLoggedIn()
handleLoggedInStatus(isLoggedIn)
if (to.meta.requiresAuth) {
if (isLoggedIn) {
if (to.meta.permission) {
if (!store.state.me) {
store.dispatch('FETCH_ME')
await sleep(2000)
}
hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
} else {
next()
}
} else {
next({ name: 'LoginForm' })
}
} else {
next()
}
})
To make it wait a "random" (2 seconds) amount of time with a little sleep() method.
I'm kinda new to the usage of async await so.. What am I missing to make await store.dispatch('FETCH_ME') work ?
Thanks in advance :)
I have a pet project with similar logic. I am not using meta
async beforeEnter(to, from, next) {
await store.dispatch('getUser')
if (store.getters.user) return next()
next('/login')
}
The logic is the following. If the user is logged in, there is a cookie in the browser and it's being sent with the store.dispatch. And if the token is valid, backend returns the user, ie if the getter returns the user, the user is logged in. I think your logic should be the same
I finally found this LINK which I didn't see before...
This allowed me to rewrite my FETCH_ME action like this :
FETCH_ME ({ commit }) {
return new Promise((resolve, reject) => {
http
.get('base/users/me/')
.then(response => {
commit('SET_ME', response.data)
resolve()
})
.catch(error => {
console.log(error)
reject(error)
})
})
}
where SET_ME is a mutation I already had :
SET_ME: (state, user) => {
state.me = user
},
Which is finally working for my case, where doing this in the router.beforeEach guard :
if (!store.state.me) await store.dispatch('FETCH_ME') effectively waits for the dispatch action to finish.

Redux-Thunk: async dispatch is not working

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
Why not simply using Promise syntax?
fetch("https://randomuser.me/api/?results=10")
.then(response => {
if (response.ok) {
// do something
}
})
.catch(e => console.log('fetch did not resolve'));

How to return the json response from the fetch API

I have a function like so:
check_auth(){
fetch(Urls.check_auth(), {
credentials: 'include',
method: 'GET'
}).then(response => {
if(response.ok) return response.json();
}).then(json => {
return json.user_logged_in;
});
}
And then I try to do this:
if(this.check_auth()){
// do stuff
} else {
// do other stuff
}
But, this.check_auth() is always undefined.
What am I missing here? I thought that within fetch's then() was where the resolved Promise object was therefore I thought that I'd get true when the user was logged in. But this is not the case.
Any help would be greatly appreciated.
Use callback
check_auth(callback){
fetch(Urls.check_auth(), {
credentials: 'include',
method: 'GET'
}).then(response => {
if(response.ok) return response.json();
}).then(json => {
callback(json.user_logged_in);
});
}
check_auth(function(data) {
//processing the data
console.log(d);
});
In React it should be easier to handle, You can call a fetch and update the state, since on every update of state using setState the render method is called you can use the state to render
check_auth = () =>{
fetch(Urls.check_auth(), {
credentials: 'include',
method: 'GET'
}).then(response => {
if(response.ok) return response.json();
}).then(json => {
this.setState({Result: json.user_logged_in});
});
}
it can be solved with the es6 aync functions
Note: the example is different from what OP asked but it gives some hints on how it should be done
const loadPosts = async () => {
const response = await fetch(/* your fetch request */);
const posts = await response.json();
return posts;
};
Async calls doesn't always resolve to be used anywhere within your app when you use .then(). The call is still async and you need to call your if-statement when you are calling your fetch. So anything that relies on the data you are fetching has to be chained to the fetch with .then().
check_auth(){
fetch(Urls.check_auth(), {
credentials: 'include',
method: 'GET'
}).then(response => {
if(response.ok) return response.json();
}).then(json => {
return json.user_logged_in;
}).then(user => checkIfAuthSuccess(user)); //You have to chain it
}
Wrapping your if-statement in a function or however your code looks like.
checkIfAuthSuccess(user){
if(user){
// do stuff
} else {
// do other stuff
}
}
Nice video about async behavior in JavaScript: Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014
To return data as JSON from Promise you should call it with await modifier from async function.
For example:
const checkAuth = async () => {
const data = await fetch(Urls.check_auth())
.then(response => response.json())
.then(json => json.user_logged_in)
return data;
}
More info about promises you can find here

Chaining Redux actions

I am new to React, Redux and JS overall. I want to know how can I dispatch and action after another is finished - Promises in correct way. My code actually works but it keeps throwing error:
readingActions.js?14b9:56 Uncaught (in promise) TypeError: dispatch(...).then is not a function(…)
This is my setup.
This is my action creator what I want chained action and where warning happends.
export function createReading(reading) {
return function (dispatch) {
dispatch({type: CREATE_READING});
return request(
`${API_URL}new`, {method: 'POST', body:JSON.stringify(reading)},
(json) => {( dispatch({type: CREATE_READING_SUCCESS, res: json}).then(dispatch(Notifications.success(showSuccess(json.book.title)))))},
(json) => { dispatch({type: CREATE_READING_ERROR400, res: json}).then(dispatch(Notifications.error(showError(json.error)))) },
(res) => { dispatch({type: CREATE_READING_ERROR500, res: res}) },
(ex) => { dispatch({type: CREATE_READING_FAILURE, error: ex}) },
)
}
}
As you can see the problem is in .then, since I dont know how to trigger action correctly.
You can also see request that is my helper function that looks like so (here I append token, return different responses):
export function request(url, options, success, error400, error, failure) {
let headers = new Headers();
headers.append("Content-Type", "application/json")
headers.append("Accept", "application/json")
options["headers"] = headers;
if (localStorage.jwtToken) {
let token = localStorage.jwtToken;
headers.append('Authorization', 'JWT '+token);
}
return fetch(url, options)
.then(res => {
if (res.status >= 200 && res.status < 300) {
res.json().then(json => {
return success(json)
})
} else if (res.status === 400) {
res.json().then(json => {
return error400(json)
})
} else {
return error(res)
}
}).catch((ex) => {
return failure(ex)
})
}
Question is how can I execute proper .then and what would be the correct way?
If you want to dispatch actions in chains you can actually implement it on your own.
Now say after analysing a bit you take a pen and paper and start write basic algorithm for how it should work and you come up with following:-
dispatch(action) => action returns function => action returns function => action returns an object(here you chain ends)
Now from above if you see that you create a middleware and keep on dispatching actions which return functions until you get an action with returns an object. This is what redux-thunk does.
So even if you try to create something of your own do that for your learning, but eventually you will come up with something like thunk or maybe some other package.
I would really say give redux-thunk a try.Also for middleware understanding I would recommend you can check redux middleware docs.

Categories

Resources