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

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));
}
};

Related

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

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
}
}

Expo Location, Expo Task Manager with Context API react native

import React, { createContext, useContext, useState, useEffect } from 'react';
import {
authenticationService,
getUserProfileService,
} from '../services/user-service';
import { getDonationHistory } from '../services/donation-service';
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as TaskManager from 'expo-task-manager';
import * as Location from 'expo-location';
import * as BackgroundFetch from 'expo-background-fetch';
const UserInfoContext = createContext();
const UserInfoProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userProfile, setUserProfile] = useState({
donorId: '',
icNo: '',
fName: '',
lName: '',
bloodType: '',
});
const [errorMessage, setErrorMessage] = useState(null);
const [location, setLocation] = useState({ latitude: '', longitude: '' });
let updateLocation = (loc) => {
setLocation(loc);
};
console.log(location);
const sendBackgroundLocation = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status === 'granted') {
const { status } = await Location.requestBackgroundPermissionsAsync();
if (status === 'granted') {
await Location.startLocationUpdatesAsync('LocationUpdate', {
accuracy: Location.Accuracy.Balanced,
timeInterval: 10000,
distanceInterval: 1,
foregroundService: {
notificationTitle: 'Live Tracker',
notificationBody: 'Live Tracker is on.',
},
});
}
}
};
const _requestLocationPermission = async () => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status == 'granted') {
let { status } = await Location.requestBackgroundPermissionsAsync();
if (status == 'granted') {
} else {
console.log('Permission to access location was denied');
}
} else {
console.log('Permission to access location was denied');
}
})();
};
sendBackgroundLocation();
const getUserProfile = () => {
return userProfile;
};
useEffect(() => {
(async () => await _requestLocationPermission())();
retrieveAuthTokens();
retrieveUserProfile();
});
let retrieveAuthTokens = async () => {
try {
const authTokens = await AsyncStorage.getItem('authTokens');
authTokens ? setIsLoggedIn(true) : setIsLoggedIn(false);
} catch (error) {
console.log(error.message);
}
};
let retrieveUserProfile = async () => {
try {
const userProfile = JSON.parse(await AsyncStorage.getItem('userProfile'));
userProfile
? setUserProfile({
donorId: userProfile.donorId,
icNo: userProfile.appUser.username,
fName: userProfile.fName,
lName: userProfile.lName,
bloodType: userProfile.bloodType,
})
: null;
} catch (error) {
console.log(error.message);
}
};
let loginUser = (values) => {
authenticationService(values)
.then(async (data) => {
if (data !== undefined && data !== null) {
const tokens = data.data;
await AsyncStorage.setItem('authTokens', JSON.stringify(tokens));
getProfile(values.icNo);
getHistories(userProfile.donorId);
setErrorMessage(null);
} else {
setErrorMessage('Wrong email/password!');
}
})
.catch((error) => console.log(error.message));
};
let logoutUser = async () => {
try {
await AsyncStorage.clear().then(console.log('clear'));
setIsLoggedIn(false);
} catch (error) {
console.log(error.message);
}
};
getProfile = (icNo) => {
getUserProfileService(icNo)
.then(async (res) => {
if (res !== undefined && res !== null) {
const profile = res.data;
await AsyncStorage.setItem('userProfile', JSON.stringify(profile));
setIsLoggedIn(true);
}
})
.catch((error) => console.log(error.message));
};
const getHistories = (userId) => {
getDonationHistory(userId)
.then(async (res) => {
if (res !== undefined && res !== null) {
const historyData = res.data;
await AsyncStorage.setItem(
'donationHistories',
JSON.stringify(historyData)
);
} else {
console.log('no data');
}
})
.catch((error) => console.log(error.message));
};
let contextData = {
loginUser: loginUser,
logoutUser: logoutUser,
isLoggedIn: isLoggedIn,
errorMessage: errorMessage,
userProfile: userProfile,
getHistories: getHistories,
};
return (
<UserInfoContext.Provider value={contextData}>
{children}
</UserInfoContext.Provider>
);
};
export const useUserInfo = () => useContext(UserInfoContext);
export default UserInfoProvider;
function myTask() {
try {
const backendData = 'Simulated fetch ' + Math.random();
return backendData
? BackgroundFetch.BackgroundFetchResult.NewData
: BackgroundFetch.BackgroundFetchResult.NoData;
} catch (err) {
return BackgroundFetch.BackgroundFetchResult.Failed;
}
}
async function initBackgroundFetch(taskName, interval = 60 * 15) {
try {
if (!TaskManager.isTaskDefined(taskName)) {
TaskManager.defineTask(taskName, ({ data, error }) => {
if (error) {
console.log('Error bg', error);
return;
}
if (data) {
const { locations } = data;
console.log(
locations[0].coords.latitude,
locations[0].coords.longitude
);
//-----------------doesnt work ----------------------------
UserInfoProvider.updateLocation({
latitude: locations[0].coords.latitude,
longitude: locations[0].coords.longitude,
});
//-----------------doesnt work ----------------------------
}
});
}
const options = {
minimumInterval: interval, // in seconds
};
await BackgroundFetch.registerTaskAsync(taskName, options);
} catch (err) {
console.log('registerTaskAsync() failed:', err);
}
}
initBackgroundFetch('LocationUpdate', 5);
I'm trying to update the location state in the UserInfoProvider and this location info will be sent to the firestore along with the userprofile details retrieved in this provider.
However, it seems that i cant access the
UserInfoProvider.updateLocation()
outside the UserInfoProvider component.
Is there anyway I can get both the userprofile info and the location info retrieved from background task together and send them to firestore?
At the moment, only the
console.log(
locations[0].coords.latitude,
locations[0].coords.longitude
);
in the background task seems to be working.
Error I got:
TaskManager: Task "LocationUpdate" failed:, [TypeError: undefined is
not a function (near '...UserInfoProvider.updateLocation...')] at
node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in
registerError at
node_modules\react-native\Libraries\LogBox\LogBox.js:60:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:34:4 in
console.error at
node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in
error at node_modules\expo-task-manager\build\TaskManager.js:143:16 in
eventEmitter.addListener$argument_1 at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:294:29 in invoke at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:155:27 in invoke at
node_modules\regenerator-runtime\runtime.js:190:16 in
PromiseImpl$argument_0 at
node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6
in tryCallTwo at
node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve at
node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11
in Promise at node_modules\regenerator-runtime\runtime.js:189:15 in
callInvokeWithMethodAndArg at
node_modules\regenerator-runtime\runtime.js:212:38 in enqueue at
node_modules\regenerator-runtime\runtime.js:239:8 in exports.async at
node_modules\expo-task-manager\build\TaskManager.js:133:57 in
eventEmitter.addListener$argument_1 at
node_modules\react-native\Libraries\vendor\emitter_EventEmitter.js:135:10 in EventEmitter#emit at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:414:4
in __callFunction at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:113:6
in __guard$argument_0 at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10
in __guard at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:4
in callFunctionReturnFlushedQueue
By reviewing UserInfoProvider context value:
let contextData = {
loginUser: loginUser,
logoutUser: logoutUser,
isLoggedIn: isLoggedIn,
errorMessage: errorMessage,
userProfile: userProfile,
getHistories: getHistories,
};
updateLocation function not available and you can't access.
You need to use useUserInfo to consume UserInfoProvider value.

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 AsyncStorage.getItem is not working. ({"_40": 0, "_55": null, "_65": 0, "_72": null})

Good day! I have this function of AsyncStorage that gets an item of a token. I used with ApolloClient to process the token but when I test it first, it seems to have an error with what will I get by AsyncStorage function.
export function jwtLogin(data) {
return async dispatch => {
const userData = {
email: data.email,
password: data.password,
};
console.log(userData);
const client = new ApolloClient({
link: new HttpLink({
uri: API_URL,
}),
cache: new InMemoryCache(),
});
client
.mutate({
mutation: loginUser,
variables: {
email: userData.email,
password: userData.password,
},
})
.then(resp => {
console.log(resp.data.tokenCreate);
console.log('Token', resp.data.tokenCreate.token);
if (resp.data.tokenCreate.token !== null) {
saveJWTTokenData(resp.data.tokenCreate.token); //from AsyncStorage save function
async function main() { //function of AsyncStorage
await AsyncStorage.getItem('jwt_token').then(item => {
return item;
});
}
console.log(main()); // returns error
Actions.push('main_loading');
} else {
const errors = resp.data.tokenCreate.errors;
{
errors.map(err => {
Alert.alert('Error.', err.message);
});
}
}
})
.catch(err => {
Alert.alert('Error.', err.message);
});
};
}
For the save storage function:
export const saveJWTTokenData = async jwt_token => AsyncStorage.setItem('jwt_token', jwt_token);
My Error Log Picture
I think your Promise is not handled correctly..
Try to add a catch after your then call like this:
.catch(err => console.log(err))
Or try to use your function like this maybe:
await getData("jwt_token")
.then(data => data)
.then(value => this.setState({ token: value })) // here it is setState but I guess you can also return
.catch(err => console.log("AsyncStorageErr: " + err));

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