Vuex: How to wait for action to finish? - vue.js

I want to implement a login method. My code for it is :
login() {
let user = {
email: this.email,
password: this.password
};
this.$store.dispatch('auth/login', user)
console.log(this.$store.getters['auth/getAuthError'])
},
Where I reach the store and dispatch the login action.
the action in the store looks like this:
login(vuexContext, user) {
return axios.post('http://localhost:8000/api/user/login', user)
.then(res => {
vuexContext.commit('setToken', res.data.token)
vuexContext.commit('setUser', res.data, {root: true})
localStorage.setItem('token', res.data.token)
Cookie.set('token', res.data.token )
this.$router.push('/')
}).catch(err => {
vuexContext.commit('setAuthError', err.response.data)
})
},
In the catch block, if an error happens, I update the state and set authError property to the error i get.
My problem is that, in the login method, the console.log statement is executed before the action is actually finished so that the authError property is the state has not been set yet.
How to fix this issue ?

Your action is returning a promise so you can console after the
promise has been resolved in then() block.
login() {
let user = {
email: this.email,
password: this.password
};
this.$store.dispatch('auth/login', user).then(() => {
console.log(this.$store.getters['auth/getAuthError'])
// this.$router.push('/') // Also, its better to invoke router's method from a component than in a store file, anyway reference of a component may not be defined in the store file till you explicity pass it
})
},
OR, you can make login an async function & wait for the action till promise
returned by action has been resolved
async login() {
let user = {
email: this.email,
password: this.password
};
await this.$store.dispatch('auth/login', user)
console.log(this.$store.getters['auth/getAuthError'])
},

You can use async-await instead of Promise.then. But my suggestion is not to use Axios inside the store. Call Axios inside the login method and then call the store. Something like this:
methods: {
async login() {
try {
const result = await axios.post('http://localhost:8000/api/user/login', this.user);
this.$store.dispatch('auth/login', result);
} catch (err) {
console.log(err);
}
}
}
And then you just need to set the Object in your store.

Related

Actions must be plain objects using Async Function for signout

Current code is not returning dispatch response
export const signOut = () => {
return async (dispatch) => {
try {
await AsyncStorage.removeItem("userToken");
dispatch({ type: "LOGOUT" });
} catch (e) {
console.log();
}
};
};
Main wallet Page for calling the action
Wallet
Returns nothing tried to add a callback response and I get nothing back...

React Hook does not set on first API call

So I am sure I am messing something up, but I am not super skilled at API.
So I am trying to make an API call to check if the user exists, if user exists then move about business, if not then do other stuff.
So my first call gets the data, and the user DOES exist, the hook is setting to true, however in my log it fails and the next API is ran. However if I do it a 2nd time, it is true...
What am I doing wrong.
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
console.log(checkUser);
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
};
I think the problem here is that setCheckUser(true) is an async operation, so there is no guarantee that the checkUser variable will turn to true right away.
Maybe you can solve this by using a useEffect block like this
//somewhere on the top of your file, below your useState statements
useEffect(()=> {
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
}, [checkUser])
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
};

Sequelize model property undefined Express.js controller after auth with passport-jwt

I am using passport-jwt to verify access to a given route in express.js, and then return a Sequelize model to the final controller. The code looks like:
The auth strategy:
const passportStrategy = passport => {
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.auth.ACCESS_TOKEN_SECRET
};
passport.use(
new Strategy(options, async (payload, done) => {
try {
const user = await User.findOne({ where: { email: payload.email }});
if (user) {
return done(null, {
user
});
}
return done(null, false);
}
catch (error) {
return done(error, false)
}
})
);
};
The route with the auth middleware
router.get('/:user_id/psy', passport.authenticate('jwt', { session: false }), patientsController.getPatientPsy);
The controller function
const getPatientPsy = async (req, res) => {
const authenticatedUser = req.user;
if (authenticatedUser.userType !== "patient") {
res.status(500).send("Big time error");
}
}
If I console.log(authenticatedUser) in the getPatientPsy() controller it successfully prints the Sequelize model with it's dataValues and so on, but when I try to access any property, be it userType or any other it consistently returns undefined.
In the passport-jwt authentication once a User has been found that matches the extracted JWT token, afaik it is returned synchronously and made it available in the req.user object, and I can print it with console.log, but why can't I access the model's properties?
I've tried to make the getPatientPsy() controller a sync function but it doesn't work either.
Thank you.
All right this is embarrassing, by default Passport.js returns the done(null, user) in the req.user property, and since I am returning { user }, I had to access through req.user.user.

how to pass query strings to get requests with fetch api

so im dispatching an action after login and using a getter to retrieve credential information that is used to initiate another fetch request. the request is going to the server correctly, and the getter is returning the appropriate data, however req.query on the server just returns [object Object]. this is the code:
getter in component:
created () {
this.$store.dispatch('user/setFriends', {email: this.userInfo.email})
},
computed: {
...mapGetters('user', {
userInfo: 'user_info'
})
}
actions:
async setFriends ({
commit
}, email) {
try {
let request = new Request(`http://localhost:3000/users?id=${encodeURIComponent(email)}`)
await fetch(request)
await (r => r.data)
await (r => commit('setFriends', r))
} catch (error) {
console.error(error.r)
}
}
route handler
router.get('/', function (req, res, next) {
console.log(req.query.id)
});
other attempt at fetch request
var url = new URL('http://localhost:3000/users')
var params = {
id: email
}
url.search = new URLSearchParams(params)
await fetch(url)
i also read this link Setting query string using Fetch GET request when consulting how to write query strings with fetch. any help would be appreciated, thanks
the problem here is that the payload when dispatching the action does not need to be passed as an object, instead of passing of
created () {
this.$store.dispatch('user/setFriends', {email: this.userInfo.email})
},
simply the value
created () {
this.$store.dispatch('user/setFriends', this.userInfo.email)
},

Request vuejs axios after auto login completed

How to execute all other request after auto login wass passed. Code example.
axios.get('personal/' + this.$store.state.username + '/config/', {headers: { Authorization: 'Token ' + this.$store.state.idToken }})
Sometimes request that receive user data (username and id) have no time to passed and commit to state, and i receive an error that username is null at state.
I have solve that problem by add in login func to save username and id in localstorage, and after in try auto login i have next code:
tryAutoLogin ({ commit, dispatch }) {
const token = localStorage.getItem('token')
if (!token) {
return
} else {
commit('getToken', {
key: token
})
const userId = localStorage.getItem('userId')
const username = localStorage.getItem('username')
if (!userId || !username) {
dispatch('getUser')
} else {
commit('getUserData', {
id: userId,
username: username.username
})
}
}
Is this way is ok? or there is any way to stop anny request to api, till the dispatch('getUser') will be passed succesfully.
example of getUser code:
getUser ({ commit, state }) {
if (!state.idToken) {
return
}
axios.get('rest-auth/user/', {headers: { Authorization: 'Token ' + state.idToken }})
.then(res => {
localStorage.setItem('username', res.data.username)
localStorage.setItem('userId', res.data.pk)
commit('getUserData', {
id: res.data.pk,
username: res.data.username
})
})
},
Plz, don't be strict i am new in FE vue js:)
First of all, make names of getters, actions, mutations and state more clean and obvious (getUser for getters and setUser for action, for example).
I recommend to create a separated auth module (place all auth logic in this module) and use it in Vuex actions or somewhere in application.
Such module should interact with the Store via Vuex getters, setters and actions (getting and setting current user auth status, for example). It makes authentification more incapsulated and clear.
In this way you'll be able to call this module's methods from any of application component and wait for the result.
In code bellow (http_auth.js) this.$auth is separated authentification module, that can set user state in Vuex and get current status. Also it use localStorage to check for saved token (user data) and tries to authorize with saved token (tryAutoLogin in your case). On fail, it makes redirect to login page.
...
methods: {
async loadInitialData () {
if (await this.$auth.init()) {
axios.get('initial-data-url').then(res => ...)
}
}
},
created () {
this.loadInitialData()
}
...
Auth methods are Promise-based, so you just can wait for resolving or rejecting before.
If you just want to use Vuex-only solution, you should use actions to call API-requests and wrap them in Promises. Also you can dispatch some action inside of other (for example, try login with saved token inside of basic login action).
Sample code (Vuex action):
LOAD_SOME_DATA ({ commit, state, getters }, id) {
return new Promise((resolve, reject) => {
if (!id) {
router.push('/')
return reject('Invalid ID passed.')
}
return axios.get(getters.GET_SOME_URL + id).then(response => {
commit('PUSH_SOME_DATA', response.data)
return store.dispatch('PROCESS_SOME_DATA').then(result => {
return resolve(response)
}, error => {
console.error('Error loading some data: ', error)
return reject(error)
})
}, error => {
router.push('/')
return reject(error)
})
})
}
Above we wrap in promise basic api-call (axios.get(getters.GET_SOME_URL + id)), then process received data (PROCESS_SOME_DATA).
Then we can use it in router, for example (or any other part of app):
store.dispatch('LOAD_SOME_DATA', to.params.id).then(result => ...)