How to handle rejected message with redux/toolkit in a right way - javascript

I have an issue about handeling rejected value. If I try to dispath function with a exist email and password everything is working, but when I enter the random email and password I can not reach the 404 error message which is coming from my backend.
This is my Slice Code
import { authService } from "../services/AuthService";
export const AuthSlice = createSlice({
name: "weather",
initialState: {
authStatus: "idle",
jwt: "",
messageCode: "",
},
reducers: {},
extraReducers: (builder) => {
//Auth Controller
builder
.addCase(authService.pending, (state) => {
state.authStatus = "loading";
})
.addCase(authService.fulfilled, (state, action) => {
console.log("fullfiled", action);
const {
data: {
code,
message_code,
result: { jwt },
},
} = action.payload;
state.messageCode = message_code;
state.jwt = jwt;
state.authStatus = "succeeded";
})
.addCase(authService.rejected, (state, action) => {
console.log("rejected", action.payload);
state.authStatus = "failed";
});
},
});
export default AuthSlice.reducer;
This is Service Code
import axios from "axios";
export const authService = createAsyncThunk(
"authController/auth",
async (auth) => {
const data = await axios({
method: "post",
url: "example.url",
headers: {},
data: {
email: auth.email,
password: auth.password,
},
});
return data;
}
);
And these are the values coming from backend. if I enter random email and password the backend returns { "code": 0, "message": "User not found.", "message_code": "LOGIN_ERROR" }
if I enter exist email and password backend returns {code:1,message_code:"LOGIN_SUCCESS",result:jwt:"token here"}
the problem is when I enter the random value authService.rejected is running but nothing is come with action from service.
I tried to write catch after axios code but this time even if I get the error value from service it comes in to authService.fulfilled and this time code can not find jwt token normally and I get a new error thats why.
I want to know what is the optimum way to handle this problem. Is there any way to get error message coming from backend in authService.rejected or do I need to write catch and need to get error message in authService.fulfilled and write if control for jwt token error.

name: "contactDetails",
initialState: {
contactInfoData: [],
loading: false,
error: null,
},
extraReducers: {
[getContactsList.pending]: (state, action) => {
state.loading = true;
},
[getContactsList.fulfilled]: (state, { payload }) => {
state.loading = false;
state.contactInfoData = payload;
},
[getContactsList.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload;
},
}
I hope this would be helpful

Related

Modifying localStorage gives A cross-origin error

I'm making a web application using reactjs, and when development is suddenly this error appears:"A cross-origin error was thrown. React doesn't have access to the actual error object in development".I AuthContext witch gives user data to localStorage :
const AuthProvider = ({ children }) => {
const token = localStorage.getItem("token");
const userInfo = localStorage.getItem("userInfo");
const expiresAt = localStorage.getItem("expiresAt");
const [authState, setAuthState] = useState({
token,
expiresAt,
userInfo: userInfo ? JSON.parse(userInfo) : {},
});
const setAuthInfo = ({ token, userInfo, expiresAt }) => {
localStorage.setItem("token", token);
localStorage.setItem("userInfo", JSON.stringify(userInfo));
localStorage.setItem("expiresAt", expiresAt);
setAuthState({
token,
userInfo,
expiresAt,
});
};
Unfortunately i have a problem with updating user details.
In context i made another function:
Edit: I forgot to write that after the function is executed userDetails changes to undefined so I get a cross-origin error
const setUserInfo = ({ userInfo }) => {
localStorage.removeItem("userInfo");
localStorage.setItem("userInfo", JSON.stringify(userInfo));
setAuthState({
userInfo,
});
};
I have rest-api route to update-details , exemplary response:
{
"success": true,
"user": {
"role": role,
"_id": id,
"username": name,
"email": email,
"createdAd": createdAt,
"__v": 0
}
}
And user rest-api response when user login, example:
{
"success": true,
"token": token,
"expiresAt": expiresAt,
"userInfo": {
"role": role,
"_id": id,
"username": name,
"email": email,
"_id": id
}
}
Updating details working when i call it but after i get cross site error.
And heres Submit function
const handleUpdate = async (info) => {
setError("");
setLoading(true);
try {
const { data } = await fetchContext.authAxios.put(
"http://localhost:5000/api/v1/auth/update-details",
info
);
const user = data.user;
console.log(user);
auth.setUserState(data);
setError(null);
setLoading(false);
} catch (err) {
setLoading(false);
// const { data } = err.response;
// setError(data.error);
console.log(err);
}
};
Ok i dumb
const setUserInfo = ({ userInfo }) => {};
should be
const setUserInfo = ( userInfo ) => {};

Vuex and firebase: The user id is undefined in the firebase database

I am creating an e-commerce web site.
Now I finished creating the new account with email and password.
And I want to insert the user email, full name, and timestamp in the database.
As you can see in the picture below, I could see the USER data in the google chrome dev console.
But when I checked the firebase database in the browser, I cannot see the user id. And instead, I see undefined in the user id column.
Now I am on the step3 process.
Add user data into database
I cannot figure out why it's happening, so I hope you can help me out.
This is my store/index.js file.
import fireApp from '#/plugins/firebase'
export const state = () => ({
user: null,
error: null,
busy: false,
jobDone: false
})
export const mutations = {
setUser (state, payload) {
state.user = payload
},
setError (state, payload) {
state.error = payload
},
clearError (state, payload) {
state.error = null
},
setBusy (state, payload) {
state.busy = payload
},
setJobDone (state, payload) {
state.jobDone = payload
},
}
export const actions = {
signUpUser({commit}, payload) {
commit('setBusy', true)
commit('clearError')
//1.Signup new user.
//2.Update firebase user profile & set local user data.
//3.Add user data into database
//4.Attach user to consumer group
let newUser = null
fireApp.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then(user => {
newUser = user
var user = fireApp.auth().currentUser;
user.updateProfile({ displayName: payload.fullname })
const currentUser = {
id: user.uid,
email: payload.email,
name: payload.fullname,
role: 'consumer'
}
console.log('USER', currentUser)
commit('setUser', currentUser)
})
.then(() => {
const userData = {
email: payload.email,
fullname: payload.fullname,
createdAt: new Date().toISOString()
}
fireApp.database().ref(`users/${newUser.uid}`).set(userData)
})
.then(() => {
commit('setJobDone', true)
commit('setBusy', false)
})
.catch(error => {
commit('setBusy', false)
commit('setError', error)
})
}
}
export const getters = {
user (state) {
return state.user
},
error (state) {
return state.error
},
busy (state) {
return state.busy
},
jobDone (state) {
return state.jobDone
}
}
This is because the promise returned by createUserWithEmailAndPassword() method resolves with an UserCredential object and not with a User one.
You should use the user property of the UserCredential, as follows:
let newUser = null
fireApp.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then(userCredential => {
newUser = userCredential.user;
//...
Note also that you don't need to call fireApp.auth().currentUser to get the user.
When using the createUserWithEmailAndPassword() method, on successful creation of the user account, this user will also be signed in to your application, so just get the user with userCredential.user, as explained above.
In addition, note that the updateProfile() method is asynchronous and returns a Promise, which you need to include in your promises chain.
So the following should do the trick (untested):
signUpUser({commit}, payload) {
commit('setBusy', true)
commit('clearError')
//1.Signup new user.
//2.Update firebase user profile & set local user data.
//3.Add user data into database
//4.Attach user to consumer group
let user = null;
fireApp.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then(userCredential => {
user = userCredential.user;
return user.updateProfile({ displayName: payload.fullname });
})
.then(() => {
const currentUser = {
id: user.uid,
email: payload.email,
name: payload.fullname,
role: 'consumer'
}
console.log('USER', currentUser)
commit('setUser', currentUser)
const userData = {
email: payload.email,
fullname: payload.fullname,
createdAt: new Date().toISOString()
}
return fireApp.database().ref(`users/${user.uid}`).set(userData)
})
.then(() => {
commit('setJobDone', true)
commit('setBusy', false)
})
.catch(error => {
commit('setBusy', false)
commit('setError', error)
})
}

Cannot initialise Vuex store from localStorage for my Auth in Nuxt

I'm using Nuxt.js and I'm trying to make my own authentication. It works fine but when I refresh the page the state is go back to initial data so I tried to initialise Vuex store from localStorage like this:
export const state = () => ({
status: '',
token: localStorage.getItem('token') || '',
loggedInUser: localStorage.getItem('user') || '',
})
but it give me this error localStorage is not defined but localStorage.setItem works fine in actions
This is the full code:
import axios from 'axios'
export const state = () => ({
status: '',
token: localStorage.getItem('token') || '',
loggedInUser: localStorage.getItem('user') || '',
})
export const getters = {
status (state) {
return state.status
},
authenticated (state) {
return !!state.token
},
token (state) {
return state.token
},
loggedInUser (state) {
return state.loggedInUser
},
}
export const mutations = {
auth_request(state) {
state.status = 'loading'
},
auth_success(state, token) {
state.status = 'success'
state.token = token
},
auth_error(state) {
state.status = 'error'
},
logout(state) {
state.status = ''
state.token = ''
state.loggedInUser = {}
},
auth_success2 (state, loggedInUser) {
state.loggedInUser = Object.assign({}, loggedInUser)
}
}
export const actions = {
login({commit}, data) {
return new Promise((resolve, reject) => {
commit('auth_request')
axios.post('http://127.0.0.1:8000/api/login', data)
.then((res) => {
const loggedInUser = Object.assign({}, res.data.data)
const token = res.data.meta.token
localStorage.setItem('token', token)
localStorage.setItem('user', loggedInUser.name)
axios.defaults.headers.common['Authorization'] = 'Bearer '+ token
commit('auth_success', token)
commit('auth_success2', loggedInUser)
this.$router.push('/')
resolve(res)
})
.catch((error) => {
commit('auth_error')
console.log(error)
reject(error)
})
})
}
}
You didn't include where this error is being thrown, but I'm going to assume it's in your server logs.
What's happening is Nuxt is initializing itself server side, begins to set up the store, and hits the localStorage declaration. The server does not have localstorage, so this will fail.
To get around this, I'd suggest using a plugin, with the .client suffix, and fetch the values from localStorage during the client side initialization:
// the .client suffix is required here to tell nuxt to only run this client side.
// ~/plugins/vuex-init.client.js
export default ({ store }) => {
const token = localStorage.getItem('token') || ''
const loggedInUser = localStorage.getItem('user') || ''
store.commit('setToken', token)
store.commit('setUser', user)
}
If you don't want to do the work yourself, I've used this before and have had great results with it.

How to display error messages from django-rest-framework in React

I am trying to implement user Registration form using Django rest framework and react, redux. I am able to register user successfully, but I am facing issue in displaying error those are provided by Django in case of error.
What I have done so far
export const AUTH_START = 'AUTH_START';
export const AUTH_SUCCESS = 'AUTH_SUCCESS';
export const AUTH_FAIL = 'AUTH_FAIL';
export const AUTH_LOGOUT = 'AUTH_LOGOUT';
Here is Reducer functionality
const initialState = {
token: null,
error: null,
loading: false
}
const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false
});
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.AUTH_START:
return authStart(state, action);
case actionTypes.AUTH_SUCCESS:
return authSuccess(state, action);
case actionTypes.AUTH_FAIL:
return authFail(state, action);
case actionTypes.AUTH_LOGOUT:
return authLogout(state, action);
default:
return state;
}
}
export default reducer;
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
}
}
Here is store functionality
export const authFail = (error) => {
return {
type: actionTypes.AUTH_FAIL,
error: error
}
}
export const authSignup = (username, email, password1, password2) => {
return dispatch => {
dispatch(authStart());
axios.post('http://127.0.0.1:8000/rest-auth/registration/', {
username: username,
email: email,
password1: password1,
password2: password2
}).then(res => {
const token = res.data.key;
const expirationDate = new Date(new Date().getTime() + 3600 * 1000);
localStorage.setItem('token', token);
localStorage.setItem('expirationDate', expirationDate);
dispatch(authSuccess(token));
dispatch(checkAuthTimeOut(3600));
}).catch(err => {
dispatch(authFail(err))
})
}
}
Here is settings.py
INSTALLED_APPS = [
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'corsheaders',
'rest_auth',
'rest_auth.registration',
'rest_framework',
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
You can full error response from server like this
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
So if you have error you can use dispatch to dispatch error something like this
dispatch(displayError(error.message));
dispatch(displayError(error.response.data));
dispatch(displayError(error.response.status));

NuxtServerInit sets Vuex auth state after reload

I'm setting a basic authentication on a Nuxt project with JWT token and cookies to be parsed by nuxtServerInit function.
On login with email/password, works as intended, setUser mutation is triggered and the appropriate user object is stored in state.auth.user.
On reload, nuxtServerInit will get the jwt token from req.headers.cookies, call the GET method and identify user.Works like a charm.
Problem starts when I hit the /logout endpoint. state.auth.user is set to false and Im effectively logged out... but If I refresh, I'm logged in again with the previous user data. Even if my cookies are properly empty (on below code, both user and cookie are undefined after logout and refresh, as expected)
So I really don't get why is my state.auth.user is back to its initial value...
store/index.js
import Vuex from "vuex";
import auth from "./modules/auth";
import axios from "~/plugins/axios";
const cookieparser = process.server ? require("cookieparser") : undefined;
const END_POINT = "api/users";
const createStore = () => {
return new Vuex.Store({
actions: {
async nuxtServerInit({ commit, dispatch}, { req }) {
let cookie = null;
console.log(req.headers.cookie)
if (req.headers.cookie) {
const parsed = cookieparser.parse(req.headers.cookie);
try {
cookie = JSON.parse(parsed.auth);
console.log("cookie", cookie)
const {accessToken} = cookie
const config = {
headers: {
Authorization: `Bearer ${accessToken}`
}
}
const response = await axios.get(`${END_POINT}/current`, config)
const user = response.data
console.log("user nuxt server init", user)
await commit('setUser', user)
} catch (err) {
// No valid cookie found
console.log(err);
}
}
}
},
modules: {
auth
}
});
};
export default createStore;
modules/auth.js
import axios from "~/plugins/axios";
const Cookie = process.client ? require("js-cookie") : undefined;
const END_POINT = "api/users";
export default {
state: {
user: null,
errors: {}
},
getters: {
isAuth: state => !!state.user
},
actions: {
login({ commit }, payload) {
axios
.post(`${END_POINT}/login`, payload)
.then(({ data }) => {
const { user, accessToken } = data;
const auth = { accessToken };
Cookie.set("auth", auth);
commit("setUser", user);
})
.catch(e => {
const error = e;
console.log(e);
commit("setError", error);
});
},
logout({ commit }) {
axios
.post(`${END_POINT}/logout`)
.then(({ data }) => {
Cookie.remove("auth");
commit("setUser", false);
})
.catch(e => console.log(e));
},
},
mutations: {
setUser(state, user) {
state.user = user;
},
setError(state, errors) {
state.errors = errors;
}
}
};
The way I logout my user is by creating a mutation called clearToken and commit to it in the action :
State :
token: null,
Mutations :
clearToken(state) {
state.token = null
},
Actions :
logout(context) {
context.commit('clearToken')
Cookie.remove('token')
}
This way, you token state revert back to null.

Categories

Resources