Vue3 Errorhandling in Vuex , how to catch error, when you indirectly dispatch a naction? - error-handling

auth(){} is to send request to the server. For, authentication ,login and signUp url is different. So I use two actions to assign the url by mode "login/signUp". then "login" and "signUp" will dispatch auth(){}
In the past, I directly put send request function fetch () in login() / signUp(). when error throw out ,I can catch it by use try {} catch{}.
Now, I want to reduct the duplicated code . The problem is , the error will be throw out by auth(), in Vue3 component, what I dispatch is not auth(), it is login() / signUp().
How can I get the error? will it be possible to pass the error through login()/signUp(),then I can get it ?
updated:
auth.vue
async setUser() {
if (this.mode === "sign up") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
const payload = {
email: this.email,
password: this.password,
};
await this.$store.dispatch("homepage/signUp", payload);
}
} else if (this.mode === "login") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
this.isLoading = true;
const payload = {
email: this.email,
password: this.password,
};
try {
await this.$store.dispatch("homepage/login", payload);
} catch (err) {
this.isLoading = false;
// console.log(err);
this.error = err.message || "something went wrong";
}
}
}
},
Vuex actions.js
async signUp (context, payload) {
context.dispatch('auth', {
...payload,
mode: 'sign up'
})
},
async login (context, payload) {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
},
async auth (context, payload) {
let url = ''
if (payload.mode === 'sign up') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[api key]'
} else if (payload.mode === 'login') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[api key]'
}
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify({
email: payload.email,
password: payload.password,
returnSecureToken: true
})
})
const resData = await res.json()
if (!res.ok) {
// console.log(resData)
const error = new Error(resData.error.message || 'Try again later ')
throw error
}
}
}

just pass error the bridge action .
add another try{} catch {}
vuex actions.js
async login (context, payload) {
try {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
} catch (err) {
throw err
}
}

Related

NextAuth authentication not working on Deployment on Vercel (Working on localhost)

Used credentials for authentication with nextauth. My code is not working on vercel deployment but working on localhost.
I used the basic cresdentials for authentication username and password but not able to get the session object after signing up from SignUpPage from the pages folder
[...nextauth].js
export default NextAuth({
session: {
jwt: true,
},
providers: [
Credentials({
async authorize(credentials) {
const client = await MongoClient.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const usersCollection = client.db().collection("users");
const user = await usersCollection.findOne({
username: credentials.username,
});
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = await compare(credentials.password, user.password);
if (!isValid) {
client.close();
throw new Error("Could not log you in!");
}
client.close();
return { username: user.username };
},
}),
],
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.user = user;
}
return token;
},
session: async ({ session, token }) => {
if (token) {
session.user = token.user;
}
return session;
},
},
secret: process.env.SECRET,
jwt: {
secret: process.env.SECRET,
encryption: true,
},
});
pages/api/auth/signup.js
async function handler(req, res) {
if (req.method !== "POST") {
return;
}
const data = req.body;
const { username, password } = data;
if (!username || !password || password.trim().length < 7) {
res.status(422).json({
message:
"Invalid input - password should also be at least 7 characters long.",
});
return;
}
const client = await MongoClient.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = client.db();
const existingUser = await db
.collection("users")
.findOne({ username: username });
if (existingUser) {
res.status(422).json({ message: "User exists already!" });
client.close();
return;
}
const hashedPassword = await hash(password, 12);
const result = await db.collection("users").insertOne({
username: username,
password: hashedPassword,
});
res.status(201).json({ message: "Created user!" });
client.close();
}
export default handler;
pages/SignUpPage.js
useEffect(() => {
getSession().then((session) => {
if (session) {
router.push("/");
} else {
setIsLoading(false);
}
});
}, [router]);
if (isLoading) {
return <p>Loading...</p>;
}
const createUser = async (username, password) => {
const response = await fetch("/api/auth/signup", {
method: "POST",
body: JSON.stringify({ username, password }),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || "Something went wrong!");
}
return data;
};
const submitHandle = async (username, password) => {
if (login) {
const result = await signIn("credentials", {
redirect: false,
username: username,
password: password,
});
if (!result.error) {
router.push("/");
}
} else {
try {
const result = await createUser(username, password);
console.log("result", result);
router.push("/");
} catch (error) {
console.log(error);
}
}
};
Looks like you have used environment variables. Make sure you have updated them with relevant values.
for example,
NEXTAUTH_URL=http://localhost:3000
to
NEXTAUTH_URL=https://youdomain.com

When I call api then redux does not wait for the response from api call and calls failure case even before getting response

ApiCall.js
Here I have called my API using Axios. I have used Promise to get the response. This response is resolved afterward and redux action calls failure case before getting this response.
export const callPostApi = async (url, body) => {
console.log('url ==> ', url, ' body ===> ', body);
return new Promise((resolve, reject) => {
try {
axios.post(url, body, config).then(
(response) => {
console.log('response ==> ', response.data);
const { success, data, message } = response.data;
if (!success) {
alert(message);
}
return resolve(response.data);
},
(error) => {
console.log('error in post api', error);
return reject(error);
}
);
} catch (error) {
console.log('error', error);
}
});
};
export const callLoginApi = async (body) => {
const resp = await callPostApi(apis.LOGIN_URL, body);
return resp;
};
ActionCreator.js
export const loginLoading = () => {
return { type: types.LOGIN_LOADING };
};
export const loginSuccess = (data) => {
return {
type: types.LOGIN_SUCCESS,
payload: data,
};
};
export const loginError = (error) => {
return { type: types.LOGIN_ERROR, error };
};
action.js
I am putting here async-await. But redux is not waiting for a response.
Here I am getting resp undefined after that API call response is received as successful.
what mistake am I making?
export const login = (body) => async (dispatch) => {
dispatch(ActionCreator.loginLoading());
try {
const resp = await callLoginApi(body);
console.log('login resp ==> ', resp);
if (resp != undefined && resp != null) {
const { success = false, message = '', data } = resp;
if (success == true) {
const obj = { user: data.user, token: data.token, isLoggedIn: true };
dispatch(ActionCreator.loginSuccess(obj));
storeJSON(CONST.user, data.user);
storeString(CONST.token, data.token);
RootNavigation.navigate(SCREENS.Tab, { screen: SCREENS.Home });
} else {
dispatch(ActionCreator.loginError(message));
}
if (message != '') {
alert(message);
}
}
} catch (error) {
dispatch(ActionCreator.loginError(error));
}
};

React Navigation taking me to home screen from login screen even if the credentials are right or wrong. Also, no error Alert is being displayed

Loginhandler is the function which evokes after clicking log in button. Below are the pieces of two files. LoginScreen is my login.js file where as Action is my action file. I have made reducer file too, but my focus is to get the username and the password from input field, send it to action file using loginhandler function and on success, opens up my Home Screen and on Error, the Alert pops up.
----------------Login Screen------------
useEffect(() => {
if (error) {
Alert.alert("An Error Occurred!", error, [{ text: "Okay" }]);
}
}, [error]);
const loginHandler = async () => {
let action = authActions.login(
formState.inputValues.username,
formState.inputValues.password
);
setError(null);
setIsLoading(true);
try {
await dispatch(action);
props.navigation.navigate("PostAuth");
} catch (err) {
setError(err);
setIsLoading(false);
}
};
-----------------ACTION FILE-------------------
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {enter code here
axios
.post("xyz.com/testApp/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedErrMsg;
if (resMsg !== "success") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
}
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
console.log(response);
})
.catch((err) => {
//console.log(err);
});
};
};
Yes I got it solved, by handling error in my action file.
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {
await axios
.post("xyz.com/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedMsg;
if (resMsg === "Error") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
} else if (resMsg === "success") {
preDefinedMsg = "success";
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
}
})
.catch((error) => {
if (error.message === preDefinedErrMsg) {
throw new Error(preDefinedErrMsg);
}
});
};
};

React-native login with API refresh HomeScreen after login with another user

After I log out with a user and then log in with another, the data of the last user persists in the main screen, I need for that Main screen to refresh after another user logs-in.
How is it possible to do that?
this is in my componentDidMount()
async componentDidMount() {
await Font.loadAsync({
'AbhayaLibre-Regular': require('../assets/fonts/AbhayaLibre-Regular.ttf'),
'AbhayaLibre-Bold': require('../assets/fonts/AbhayaLibre-Bold.ttf'),
});
this.setState({ fontLoaded: true });
this.authCheck()
if (this.state.loggedIn == "false") {
this.props.navigation.navigate("Login");
} else {
this.authUser();
this.fetchSubjects();
}
}
It puzzles me because I'm not sure how to run that code again AFTER a new user logs-in after another logged out.
Code snipped of the login:
<Button icon="send" mode="contained" text="#1ebc61" color="white" onPress={() => {
axios.post('http://homewrk.test/api/auth/login', {
email: this.state.email,
password: this.state.password
}).then(res => {
this.setState({ token: res.data.access_token });
this.storeKey();
this.props.navigation.navigate("Home");
}).catch(() => {
this.setState({ error: true })
})
}}>
authCheck()
async authCheck() {
const token = await AsyncStorage.getItem('access');
const access = 'Bearer ' + token;
console.log(access);
axios.get(`http://homewrk.test/api/check`, {
headers: {
'Authorization': access,
}
}).then(res => {
const isLoggedIn = res.data;
this.setState({ loggedIn: isLoggedIn });
})
}
storing the key:
storeKey = async () => {
try {
await AsyncStorage.setItem('access', this.state.token);
console.log("done");
} catch (e) {
console.log(e);
}
}
async componentDidMount() {
await Font.loadAsync({
'AbhayaLibre-Regular': require('../assets/fonts/AbhayaLibre-Regular.ttf'),
'AbhayaLibre-Bold': require('../assets/fonts/AbhayaLibre-Bold.ttf'),
});
this.focusListener = this.props.navigation.addListener("willFocus", () => {
this.authCheck()
if (this.state.loggedIn == "false") {
this.props.navigation.navigate("Login");
} else {
this.authUser();
this.fetchSubjects();
}
});
}
componentWillUnmount() {
this.focusListener.remove();
}

Getting a 401 error when trying to create a new post

I am trying to create a post using an app built in react native but everytime I try creating it gives me a 401 error after I have already logged in. I assume it isn't getting a token from AsyncStorage. I need helping.
This is the ItemContext where the functionality for creating a post-
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const itemReducer = (state, action) => {
switch (action.type) {
case "fetch_items":
return action.payload;
case "create_item":
return { errorMessage: "", item: action.payload };
default:
return state;
}
};
const fetchItems = dispatch => async () => {
const response = await sellerApi.get("/api/items");
console.log(response.data);
dispatch({ type: "fetch_items", payload: response.data });
};
const createItem = dispatch => async (
title,
category,
detail,
condition,
price
) => {
try {
const response = await sellerApi.post("/api/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
console.log(response.data);
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
export const { Provider, Context } = createDataContext(
itemReducer,
{ createItem, fetchItems },
[]
);
this is the AuthContext where the signin and signup functionality is located and the AsyncStorage is used. Let me know if you guys need to see the node function for Auth.
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const authReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "signup":
return { errorMessage: "", token: action.payload };
case "signin":
return { errorMessage: "", token: action.payload };
case "fetch_user":
return action.payload;
case "clear_error_message":
return { ...state, errorMessage: "" };
case "signout":
return { token: null, errorMessage: "" };
default:
return state;
}
};
const tryLocalSignin = dispatch => async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
dispatch({ type: "signin", payload: token });
navigate("Home");
} else {
navigate("loginFlow");
}
};
const clearErrorMessage = dispatch => {
dispatch({ type: "clear_error_message" });
};
const signup = dispatch => async ({ name, phone, email, password }) => {
try {
const response = await sellerApi.post("/api/users", {
name,
phone,
email,
password
});
//this is the other place the error might be happening i need this to save in the phone local storage
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signup", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
const signin = dispatch => async ({ email, password }) => {
try {
const response = await sellerApi.post("/api/auth", {
email,
password
});
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signin", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
// const fetchUser = dispatch => async () => {
// const response = await sellerApi.get("/auth");
// dispatch({ type: "fetch_user", payload: response.data });
// };
//need to get the users info to display it in the accountScreen
const signout = dispatch => async () => {
await AsyncStorage.removeItem("token");
dispatch({ type: "signout" });
navigate("loginFlow");
};
export const { Provider, Context } = createDataContext(
authReducer,
{ signup, signin, signout, tryLocalSignin },
{ token: null, errorMessage: "" }
);
This is the backend for the Auth function that makes sure the user is logged in before begin able to send a post request----
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function (req, res, next) {
const token = req.header("x-auth-token");
if (!token) return res.status(401).send("Access denied");
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
res.status(400).send("Invalid token.");
}
}
this is where the post request for when you signup and login is pretty much similar-
router.post("/", async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
user = new User(_.pick(req.body, "name", "phone", "email", "password"));
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
await user.save();
const token = user.generateAuthToken();
res.header("x-auth-token", token).send(token);
});
PLEASE HELP
Importing Async storage like this import {AsyncStorage} from 'react-native'; has been deprecated. You can check here async storage .
Thats why i suppose the AsyncStorage is not working, try downloading this rn-community-async-storage . package first and then import AsyncStorage like
import AsyncStorage from '#react-native-community/async-storage';
hope it helps. feel free for doubts