Vue js calling action inside action - javascript

I have the following actions in my Vuex store:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ commit }) {
HTTP.get('v1/my_account.json').headers({'Authorization': ('Token token=' + localStorage.getItem('access_token'))})
.then(response => {
commit('SET_USER', response.data)
})
}
}
I want to launch myAccount action when loginUser succeeds. How can I do that?
I've tried something like this:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
console.log('dupa')
// Do this when loginUser finished
})
}
}
but this not works...

actions receive the context object, so you can simply either pass the entire object or add dispatch to your destructuring assignment :
const store = new Vuex.Store({
actions: {
foo(context) {
console.log('foo called');
},
bar({dispatch}) {
setTimeout(() => dispatch('foo'), 1000)
}
}
});
Here's the JSFiddle: https://jsfiddle.net/y1527vxh/

Since vue actions can be asynchronous you can add dispatch handler to an action to call another action when it is done;
export const actions = {
loginUser ({ commit, state }, params) {
... // some http request or what would you like to do
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
...
// Do this when loginUser finished
})
},
}
I am doing autentication in my projects like this, i am using axios btw:
loginUser ({ dispatch, commit, state }, payload) {
let loginData = {
username: payload.username,
password: payload.password
}
return axios.post(state.url, loginData)
.then((res) => {
// You can console.log(res.data) to see if your token data is fine
window.localStorage.setItem('AuthTokens', JSON.stringify(res.data))
dispatch('myAccount', { tokens: res.data })
})
.catch((err) => {
// Error handling...
})
},
myAccount ({ commit, state }, { tokens }) {
let headerOptions = {
// Header options with tokens.access_token...
}
return axios.get(state.url, headerOptions)
.then((res) => {
// You have the user data
console.log(res.data)
})
.catch((err) => {
// Error handling...
})
}

Related

how to write async storage value return function in react-native

how to write a method to return async storage value in react native.
I have done login authentication using Context API.
AuthContext.js
import React, { createContext } from "react";
import { useState, useEffect } from "react";
import { Alert } from "react-native";
import AsyncStorage from '#react-native-async-storage/async-storage';
import NetInfo from "#react-native-community/netinfo";
import { BASE_URL } from "../constants/Const";
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
// loading & usertoken hooks
const [isLoading, setIsLoading] = useState(false);
const [userToken, setuserToken] = useState(null);
//login method
const login = async (email, password) => {
setIsLoading(true);
//fetch method to get access token
fetch(`${BASE_URL}/sign_in`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
password: password,
})
})
.then((res) => res.json())
.then((json) => {
//console.log("login auth ", json)
if (json.responseCode == 200) {
try {
setuserToken(json.responseData.access_token);
//storing usertoken value in react-native encrypted storage
AsyncStorage.setItem("userToken", json.responseData.access_token)
//console.log("user token",userToken);
}
catch (error) {
console.log("Error while saving user token data", userToken);
}
}
//showing invalid email & password alert messaqge
else {
Alert.alert('Invalid email or password.');
return;
}
})
.catch((error) => {
console.error(error);
});
setIsLoading(false);
}
//logout method
const logout = async () => {
setIsLoading(true);
//setting user token as null
setuserToken(null);
try {
//removing usertoken value in react-native encrypted storage
await AsyncStorage.removeItem("userToken");
} catch (error) {
// There was an error on the native side
console.log("Error while removing data", error);
}
setIsLoading(false);
}
// checking user is already logged in each time when app starts
const isLoggenIn = async () => {
try {
setIsLoading(true);
//let userToken = await EncryptedStorage.getItem("userToken");
let userToken = await AsyncStorage.getItem("userToken");
//console.log("accesed user token",userToken);
setuserToken(userToken);
setIsLoading(false);
} catch (error) {
console.log("Error retrieving data", error);
}
}
const [connected, setConnected] = useState(true);
useEffect(() => {
isLoggenIn();
// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
// console.log("Connection type", state.type);
// console.log("Is connected?", state.isConnected);
setConnected(state.isConnected);
});
//clean up function
return () => unsubscribe();
}, [])
return (
<AuthContext.Provider value={{ login, logout, isLoading, userToken ,connected,}}>
{children}
</AuthContext.Provider>
)
}
in Redux slice file I want access token values to make a fetch request to a server.i defined getAccessToken Method to return accessToken value but it is not returning value
DataSlice.js
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { useState } from 'react';
import { Alert } from 'react-native';
import { BASE_URL } from '../constants/Const';
import AsyncStorage from '#react-native-async-storage/async-storage';
//const accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjM2YTFlNTc4YWNlNTRjM2E5OWE4YWI2IiwiZW1haWwiOiJhZG1pbkBnbWFpbC5jb20iLCJpYXQiOjE2Njg0MDY2MzgsImV4cCI6MTY2ODQ2MDYzOH0.i_GHy2K91I0_159AIpQ4m2eFUmyXYFmF3_0sQ-o_x-w";
//user token function
const getAcessToken = async () => {
const token = await AsyncStorage.getItem('userToken');
return token;
}
var userToken = getAcessToken();
// get and delete method request options
const getDeleteRequestOptions = (methodType) => {
return {
method: methodType,
headers: {
'Content-Type': 'application/json',
'autherization': userToken
}
}
}
// save and edit method request options
const saveEditRequestOptions = (methodType, data) => {
console.log("img uri",data.imgUri)
const imgData = new FormData();
imgData.append('first_name', data.firstName);
imgData.append('last_name', data.lastName);
imgData.append('phone', data.phoneNo);
imgData.append('email', data.email);
imgData.append('image', { uri: data.imgUri, name: 'image', type: 'image/jpg' });
return {
method: methodType,
headers: {
'Content-Type': 'multipart/form-data',
'autherization': userToken
},
body: imgData
}
};
// fetch data
export const getData = createAsyncThunk('fetch', async ({pageNo,limit}) => {
return fetch(`${BASE_URL}/list_profile?page_number=${pageNo}&limit=${limit}`, getDeleteRequestOptions('GET'))
.then((response) => response.json())
.then((json) => {
//returning json receive array
if(pageNo === 0 && limit === 0){
return {data:json.receive,fetchAllData:true};
}
return {data:json.receive,fetchAllData:false};
})
.catch((error) => {
console.error(error);
});
});
// delete data
export const deleteData = createAsyncThunk('delete', async ({id}) => {
return fetch(`${BASE_URL}/delete_profile/${id}`, getDeleteRequestOptions('DELETE',userToken))
.then((res) => res.json())
.catch((error) => {
console.error(error);
});
});
// save data
export const saveData = createAsyncThunk('save', async (data) => {
return fetch(`${BASE_URL}/add_profile`, saveEditRequestOptions('POST', data))
.then((res) => res.json())
.then((json) => {
if (json.responseCode === 211) {
Alert.alert('Input Error', json.responseMessage, [
{ text: "OK" }
])
return;
}
console.log("save responese message ", json.responseMessage);
})
.catch((error) => {
console.error(error);
});
});
// edit data
export const editData = createAsyncThunk('edit', async (data) => {
return fetch(`${BASE_URL}/update_profile/${data.id}`, saveEditRequestOptions('PUT', data))
.then((res) => res.json())
.then((json) => {
console.log("edit data response message ", json.responseMessage);
})
.catch((error) => {
console.error(error);
});
});
const initialState = {
masterData: [],
filteredData: [], //array to implement search
allData:[],
imgurl: '',
};
export const dataSlice = createSlice({
name: 'crud',
initialState,
reducers: {
filterData: (state, action) => {
state.filteredData = action.payload;
},
selectedImage: (state, action) => {
state.imgurl = action.payload;
},
},
extraReducers: {
// get data
[getData.pending]: (state, action) => {
console.log('fetching data is pending');
},
[getData.fulfilled]: (state, action) => {
console.log('data fetched successfully')
if (!action.payload) {
Alert.alert('Network error', 'Data Fetching is Failded Please try Again later.', [
{ text: "OK" }
])
return;
}
console.log(action.payload.fetchAllData)
if(action.payload.fetchAllData){
//console.log("inside fetch all data")
state.allData = action.payload.data;
}
state.masterData = action.payload.data;
state.filteredData = action.payload.data;
},
[getData.rejected]: (state, action) => {
console.log('fetching request rejected');
},
// delete data
[deleteData.pending]: (state, action) => {
console.log('delete data is pending');
},
[deleteData.fulfilled]: (state, action) => {
console.log('data deleted successfully');
},
[deleteData.rejected]: (state, action) => {
console.log('data delete request rejected');
Alert.alert('Delete Data Failure', 'Deleting Data Failed. Please try Again later.', [
{ text: "OK" }
])
},
// save data
[saveData.pending]: (state, action) => {
console.log('saving data is pending');
},
[saveData.fulfilled]: (state, action) => {
console.log('data saved successfully');
},
[saveData.rejected]: (state, action) => {
console.log('data save request rejected');
Alert.alert('Data Save Failure', 'Data Save Failed. Please try Again later.', [
{ text: "OK" }
])
},
//edit data
[editData.pending]: (state, action) => {
console.log('edit data is pending');
},
[editData.fulfilled]: (state, action) => {
console.log('data edited successfully');
},
[editData.rejected]: (state, action) => {
console.log('edit data request rejected');
Alert.alert('Data Edit Failure', 'Edit data Failed. Please try Again later.', [
{ text: "OK" }
])
},
},
})
// Action creators are generated for each case reducer function
export const { filterData, selectedImage } = dataSlice.actions;
export default dataSlice.reducer
i want to get access token values
The reason is that you are not calling getAccessToken() with await like this:
var userToken = await getAcessToken();
since it is asynchronous function.

How can I use the current status of redux after the thunks and actions have finished?

How can I use the current status of redux after the thunks and actions have finished? The problem is in the handleSubmit function if I register a user with errors, it updates the status of redux with the message "Email already registered", but when accessing the state in the dispatch promise sends me a wrong state, without the message.
Function hanldeSubmit
const handleSubmit = (e) => {
e.preventDefault()
const form = {
name: e.target[0].value,
email: e.target[1].value,
password: e.target[2].value,
confirmPassword: e.target[3].value
}
const { name, email, password } = form
if (isFormValid(form)) {
//TODO: FIX IT synchronize redux with errors
dispatch( startRegisterUser(name, email, password) ).then(() => {
console.log(state)
})
}
}
register action and thunk
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
dispatch(startLoadingAction())
return firebase.auth().createUserWithEmailAndPassword(email, password)
.then(async ({ user }) => {
await user.updateProfile({
displayName: name,
photoURL: ''
})
dispatch(registerUserAction(user.uid, user.displayName))
})
.catch(e => {
if (e.code === "auth/email-already-in-use") {
dispatch(errorAction("Email already registered"))
} else {
dispatch(errorAction("Unexpected error"))
}
})
.then(() => {
dispatch(finishLoadingAction())
console.log("finished dispatch's", state())
return
})
}
}
export const registerUserAction = (uid, displayname) => {
return {
type: types.register,
payload: {
uid,
displayname
}
}
}
console logs
I want to get the status of the first console log but in the handlesubmit function
You should handle the errorAction in the reducer, update the ui store slice with the error message. And, you need to return the state() in the promise in the thunk function. Then, you will get the whole state inside the handleSubmit event handler.
E.g.
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
function errorAction(message) {
return {
type: 'ERROR',
payload: message,
error: true,
};
}
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
return Promise.reject({ code: 'auth/email-already-in-use' })
.catch((e) => {
if (e.code === 'auth/email-already-in-use') {
dispatch(errorAction('Email already registered'));
} else {
dispatch(errorAction('Unexpected error'));
}
})
.then(() => state());
};
};
export const registerUserAction = (uid, displayname) => {
return {
type: 'REGISTER',
payload: {
uid,
displayname,
},
};
};
function rootReducer(state = { ui: { error: '' } }, action) {
switch (action.type) {
case 'ERROR':
return { ui: { error: action.payload } };
default:
return state;
}
}
const store = createStore(rootReducer, applyMiddleware(thunk));
function handleSubmit() {
store
.dispatch(startRegisterUser('name', 'example#gmail.com', '123') as any)
.then((state) => {
console.log('handleSubmit state: ', state);
});
}
// triggered by user submit event
handleSubmit();
Output:
handleSubmit state: { ui: { error: 'Email already registered' } }

Verify and refresh JWT access token in using Vue Router and Vuex

I've created simple VueCLI auth module using axios and Vuex.
In store.js I've got all logic for tokens using api from session.js:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import sessionSerivce from '#/services/session.js'
Vue.use(Vuex)
Vue.use(require('vue-cookies'))
export const store = new Vuex.Store({
state: {
status: '',
accessToken: $cookies.get('accessToken') || '',
refreshToken: $cookies.get('refreshToken') || '',
user: $cookies.get('user') || '',
},
actions: {
login({ commit }, data) {
return new Promise((resolve, reject) => {
commit('auth_request')
sessionSerivce
.logIn(data)
.then((resp) => {
const commitData = {
accessToken: resp.data.access_token,
refreshToken: resp.data.refresh_token,
user: resp.data.user,
}
$cookies.set('accessToken', commitData.accessToken)
$cookies.set('refreshToken', commitData.refreshToken)
$cookies.set('user', JSON.stringify(commitData.user))
axios.defaults.headers.common['Authorization'] =
commitData.accessToken
commit('auth_success', commitData)
resolve(resp)
})
.catch((err) => {
commit('auth_error')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
reject(err)
})
})
},
verifyToken({ commit, state }) {},
register({ commit }, data) {
return new Promise((resolve, reject) => {
commit('auth_request')
sessionSerivce
.register(data)
.then((resp) => {
const commitData = {
accessToken: resp.data.access_token,
refreshToken: resp.data.refresh_token,
user: resp.data.user,
}
$cookies.set('accessToken', commitData.accessToken)
$cookies.set('refreshToken', commitData.refreshToken)
$cookies.set('user', JSON.stringify(commitData.user))
axios.defaults.headers.common['Authorization'] =
commitData.accessToken
commit('auth_success', commitData)
resolve(resp)
})
.catch((err) => {
commit('auth_error')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
reject(err)
})
})
},
logout({ commit }) {
return new Promise((resolve, reject) => {
commit('logout')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
delete axios.defaults.headers.common['Authorization']
resolve()
})
},
},
mutations: {
auth_request(state) {
state.status = 'loading'
},
auth_success(state, commitData) {
state.status = 'success'
state.accessToken = commitData.accessToken
state.refreshToken = commitData.refreshToken
state.user = commitData.user
},
auth_error(state) {
state.status = 'error'
},
refresh_token(state, accessToken) {
state.accessToken = accessToken
},
logout(state) {
state.status = ''
state.accessToken = ''
state.refreshToken = ''
state.user = ''
},
},
getters: {
isLoggedIn: (state) => {
return !!state.accessToken
},
authStatus: (state) => state.status,
},
})
In main.js I use this function to check:
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (store.getters.isLoggedIn) {
next()
return
}
next('/login')
} else next()
})
The problem is that code above checks only if access token exists in Vuex. I want to verify using api before any route, that requires auth and if it's not successfully I want to refresh It with api using refresh token. If both are unsuccessful(access and refresh tokens are both invalid) user gonna log out.
Example route which requires auth:
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
},
I've tried code like this:
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (store.state.accessToken) {
await store.dispatch('verifyToken')
if (store.getters.isLoggedIn) {
next()
return
}
}
next('/login')
} else next()
})
Action in Vuex:
verifyToken({ commit, state }) {
const accessToken = state.accessToken
const refreshToken = state.accessToken
sessionSerivce
.verifyToken(accessToken)
.then((resp) => {})
.catch((err) => {
sessionSerivce
.refreshToken(refreshToken)
.then((resp) => {
console.log('Refreshuje token')
const accessToken = resp.data.access_token
localStorage.setItem('accessToken', accessToken)
axios.defaults.headers.common['Authorization'] = accessToken
commit('refresh_token', accessToken)
})
.catch((err) => {
commit('logout')
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
delete axios.defaults.headers.common['Authorization']
})
})
},
Note that in code above i used localstorage but i've changed my mind and I'm using cookie, as You can see in previous code.
Unfortunately this code didn't work as expected - if (store.getters.isLoggedIn) { next(); return; } is starting to execute before await store.dispatch('verifyToken') ends, which is bad.
Any ideas?

Updating the component when data changes (vuejs)

I have a CRUD component and the add user (userRegister) is giving me problems. I am able to successfully add a user, however the view doesn't refresh and the new user doesn't show unless I refresh the page.
Here's the method fired on the Submit button in my Vue Component
onSubmit() {
if (this.password === this.confirmpassword) {
this.$store
.dispatch("users/userRegister", {
operatorNumber: this.operatorNumber,
operatorName: this.operatorName,
password: this.password,
roles: this.operatorRole
})
.then(({ status }) => {
UIkit.offcanvas("#newEmployee").hide();
})
.catch(error => {
console.log("Error: ", error);
});
}
this.resetForm();
},
and my store
import { axiosInstance } from "boot/axios";
export default {
namespaced: true,
state: {
operators: []
},
getters: {
operators: state => {
return state.operators;
}
},
actions: {
async userRegister({ commit }, payload) {
return new Promise((resolve, reject) => {
axiosInstance
.post("user/create", payload)
.then(({ data, status }) => {
if (status === 200) {
resolve(true);
commit("addUser", payload);
}
})
.catch(error => {
reject(error);
console.log("Error: ", error);
});
});
},
},
mutations: {
addUser: (state, operators) => state.operators.splice(state.operators.length, 0, operators)
}
};
What am I missing? Thanks

Undefined is not an object (evaluating 'res.data'). What is this?

I need to post data from a user to my database (firebase), but when I click on Touchable Opacity, my app returns this error:
Single Ocurrence
Possible Unhandled Promise rejection (id:0): TypeError: undefined is not an object (evaluating 'res.data'
My code:
import {USER_LOGGED_IN, USER_LOGGED_OUT } from './actionTypes'
import axios from 'axios'
const authBaseURL ='https://www.googleapis.com/identitytoolkit/v3/relyingparty'
const API_KEY ='my api key is here'
export const login = user => {
return {
type: USER_LOGGED_IN,
payload: user
}
}
export const logout = () => {
return {
type: USER_LOGGED_OUT
}
}
export const createUser = user => {
return dispatch => {
axios.post (`${authBaseURL}/signupNewUser?key=${API_KEY}`, {
email: user.email,
password: user.password,
returnSecureToken:true
})
.catch(err => console.log(err))
.then (res => {
if (res.data.localId) {
axios.put(`/users/${res.data.localId}.json`, {
name: user.name,
tipouser: user.tipouser,
telcelular: user.telcelular,
telfixo: user.telfixo,
facebookuser: user.facebookuser,
instagramuser: user.instagramuser
})
.catch(err => console.log(err))
.then(res => {
console.log('Usuário criado com sucesso')
})
}
})
}
}
react-native: 0.60.4
axios: 0.19.0
react-redux: 7.1.1
redux: 4.0.4"

Categories

Resources