I have an action that currently works fine using .then but when I try and convert it to async ... await it suddenly can't access the first parameter of the function. The second parameter still works fine though.
Current function that works fine:
export const signInUser = (email, password) => {
return (dispatch) => {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
console.log('signed in')
const { uid, email } = firebase.auth().currentUser
dispatch({ type: 'SIGN_IN', uid, email })
return dispatch(fetchAllData())
})
.catch(error => {
throw (error)
})
}
};
New function that doesn't work because 'email' is undefined.
export const signInUser = (email, password) => {
return async (dispatch) => {
console.log('testing')
console.log(password)
console.log('testing', email, password)
await firebase.auth().signInWithEmailAndPassword(email, password)
console.log('signed in')
const { uid, email } = firebase.auth().currentUser
dispatch({ type: 'SIGN_IN', uid, email })
return dispatch(fetchAllData())
}
};
The first console log of console.log('testing') works fine and outputs the string 'testing'. The second console log of console.log(password) also works fine and prints the inputted password. But the third console log console.log('testing', email, password) doesn't get shown at all.
This is how the function is called:
Parent component passes it to child component:
submit={(email, password) => dispatch(signInUser(email, password))}
Child component calls it:
const submitForm = (event) => {
event.preventDefault();
if (validForm) {
setLoading(true)
submit(email, password)
.catch(err => {
setLoading(false)
console.log('catch:', err.code)
setError(err.code)
})
}
}
The output I am receiving is catch: undefined.
Also, if I change the function to this:
export const signInUser = (email, password) => {
const userEmail = email
return async (dispatch) => {
console.log('testing')
console.log(password)
console.log('testing', userEmail, password)
await firebase.auth().signInWithEmailAndPassword(userEmail, password)
console.log('signed in')
const { uid, email } = firebase.auth().currentUser
dispatch({ type: 'SIGN_IN', uid, email })
return dispatch(fetchAllData())
}
};
Then it works fine. But I don't know why I would need to change it like this?
Thanks
Ok so the reason it wasn't working was because on the next line I was declaring email like this const { uid, email } = firebase.auth().currentUser
which was overriding the function parameter of email and therefore giving me the error that i was using email before it was declared.
Related
I have a react app where I use the useContext and useReducer hooks for the login and storage. While the login part works, what I want achieve is to redirect user to a specific page post successful login. I am using react-router#6 and tried to use useNavigate() to navigate user to particular route though it doesn't seem to work.
const AuthService = async (dispatch) => {
const MSAL_CONFIG = {} // populate MSAL config for Microsoft Graph API for AD auth
const msalInstance = new msal.PublicClientApplication(MSAL_CONFIG);
try {
const loginResponse = await msalInstance.loginPopup(scopes);
var username = loginResponse.account.username;
var userid = username.slice(0, username.indexOf("#"));
const loginData = {
auth_token: loginResponse.idToken,
user: {
name: loginResponse.account.name,
id: userid,
email: username,
},
};
const sessionData = {
user_id: userid,
id_token: loginResponse.idToken,
access_token: loginResponse.accessToken,
}
sessionStorage.setItem("currentUser", JSON.stringify(loginData));
dispatch({ type: "LOGIN_SUCCESS", payload: loginData });
return { loginData: loginData, error: null };
// dispatch({ type: 'LOGIN_SUCCESS', payload: loginData });
//sessionStorage.setItem('currentUser', JSON.stringify(data));
} catch (err) {
console.log("+++ Login error : ", err);
dispatch({ type: "LOGIN_ERROR", error: err });
return { loginData: null, error: err };
}
};
In my header.jsx, I have below code to handle the login button. It makes a call to the above AuthService. The code post AuthService() call, i.e. the if block, doesn't take effect, so user never gets redirected to the dashboard page.
const handleLogin = async () => {
await AuthService(dispatch)
console.log("userDetails.token : " + userDetails.token)
if (Boolean(userDetails.token)) {
navigate("/dashboard");
}
};
If I'm correct in understanding that this AuthService function eventually resolves and that the dispatched LOGIN_SUCCESS action updates the userDetails variable that is selected from the auth context state, then I think you have all that you need and are close to a working solution. The issue is that the userDetails value from the render cycle the handleLogin is called in is closed over in callback scope, it will never be a different value. If the userDetails.token value is falsey when handleLogin is called, it will remain falsey in the entire callback scope.
The AuthService function appears to return the same loginData object that is passed in the dispatched LOGIN_SUCCESS action to the store. handleLogin should await this value and conditionally navigate.
const AuthService = async (dispatch) => {
...
try {
const { account, idToken } = await msalInstance.loginPopup(scopes);
const { name, username } = account;
const userid = username.slice(0, username.indexOf("#"));
const loginData = {
auth_token: idToken,
user: {
name,
id: userid,
email: username,
},
};
...
sessionStorage.setItem("currentUser", JSON.stringify(loginData));
dispatch({ type: "LOGIN_SUCCESS", payload: loginData });
return { loginData, error: null }; // <-- return value
} catch (error) {
dispatch({ type: "LOGIN_ERROR", error });
return { loginData: null, error }; // <-- return value
}
};
const handleLogin = async () => {
const { loginData } = await AuthService(dispatch);
if (loginData && loginData.auth_token) { // or loginData?.auth_token
navigate("/dashboard", { replace: true });
}
};
Please help me, I entered this code below but It doesn't work. When I give the correct email and password I get the error message ->
Here is my code:
const loginUser = (email, password, location, history) => {
setIsLoading(true);
signInWithEmailAndPassword(auth, email, password)
.then(() => {
const destination = location?.state?.from || "/";
history.replace(destination);
setError("");
})
.catch((error) => {
setError(error.message);
})
.finally(() => setIsLoading(false));
};
I am working on authentication for my react native app.
The problem I am having is that the signInUser function seems to be executing in the wrong order. I want the signIn function to fully execute before moving on.
However, that doesn't seem to be happening as I am getting this response in my console, with the "undefined" coming from console.log(response) in the SignInScreen.
Here is the console:
undefined
16d0707a3508a9b43b8c36c8574ca73d8b4b26af
I have this function in the SignInScreen.js
import { signIn } from "../services/authService";
import { useAuthDispatch } from "../contexts/authContext";
const SignInScreen = ({ navigation }) => {
const dispatch = useAuthDispatch();
const [signInLoading, setSignInLoading] = useState(false);
const signInUser = async (values) => {
const { email, password } = values;
setSignInLoading(true);
signIn(email, password)
.then((response) => {
console.log(response);
dispatch({
type: "SIGN_IN",
token: response,
});
})
.catch((e) => {
console.log(e);
})
.finally(() => setSignInLoading(false));
};
And this is my authService.js:
import axios from "axios";
const signIn = async (email, password) => {
axios
.post("http://127.0.0.1:8000/rest-auth/login/", {
username: email,
email: email,
password: password,
})
.then((response) => {
console.log(response.data.key);
return response.data.key;
})
.catch((error) => {
return error;
});
};
How can I fix this?
Uncaught (in promise) Error: Request failed with status code 400
I need to make a page request to the database for logging into the system, but I'm already too confused and don't know how to remove this error.
Before that there was the error "Actions must be plain objects. Use custom middleware for async actions."
After that I connected Redux-Thunk and the current error appeared.
Actions
export const auth = (email, password, isLogin) => {
return async(dispatch) => {
dispatch(authData())
let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyAU8gNE0fGG8z9zqUyh68Inw9_RzljhCCs'
if (isLogin) {
url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyAU8gNE0fGG8z9zqUyh68Inw9_RzljhCCs'
}
const response = await axios.post(url, authData)
console.log(response.data)
}
}
const authData = (email, password, returnSecureToken = true) => ({
type: 'LOGIN',
email,
password,
returnSecureToken
})
Component
loginHandler = () => {
this.props.auth(
this.props.AuthMail,
this.props.AuthPass,
true
)
}
registerHandler = () => {
this.props.auth(
this.props.AuthRegMail,
this.props.AuthRegPass,
false
)
}
const mapDispatchToProps = dispatch => {
return {
auth: (email, password, isLogin) => dispatch(auth(email, password, isLogin))
}
}
// You forgot to add the arguments to authData function
dispatch(authData())
// Here you are passing in a function as the second argument
const response = await axios.post(url, authData)
Should probably be something like this:
export const auth = (email, password, isLogin) => {
return async (dispatch) => {
const url = isLogin ? 'example.com/login' : 'example.com/signup';
const response = await axios.post(url, {
email,
password,
returnSecureToken: true,
});
console.log(response.data);
// Handle this action somewhere to store the signed in user data in redux
dispatch({
type: "LOGIN",
payload: response.data
})
}
}
Why can't I navigate to the screen "App" in the following code. I get the error:
Cannot read property 'navigate' of undefined
async signIn() {
const { username, password } = this.state
await Auth.signIn(username, password)
.then(user => {
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
})
.catch(err => {
console.log('Error when signing in: ', err)
Alert.alert('Error when signing in: ', err)
})
}
}
You are mixing promises and await. You either await for the sign in, or remove the async/await and treat it like a promise. Also, it could be that you haven't binded the function so "this" points to undefined.
Either:
async signIn = () => {
const { username, password } = this.state
let user = await Auth.signIn(username, password);
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
}
}
Or
signIn = () => {
const { username, password } = this.state
Auth.signIn(username, password)
.then(user => {
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
})
.catch(err => {
console.log('Error when signing in: ', err)
Alert.alert('Error when signing in: ', err)
})
}
}