In my react web app, login-logout functionality is implemented using context-API and hooks. Everything seems to work fine except for the fact that I have to click the 'Login' button twice to push the user to the dashboard.
The statement, return <Redirect to="/dashboard /> does not work, instead I have to use history.push('/dashboard'), which does not seems to be a better option to me while login.
Here is the working snippet :
https://codesandbox.io/s/wizardly-worker-mqkex?file=/src/AuthContext.js
Also, I need some suggestions for the best practise to fetch the logged user details in other components. Using localstorage or global context API state, which of them serves as the best option for the same ?
Any help to resolve this, appreciated :)
Well, it boils down to the simple fact that your context is not updated when you do your check. The simplest solution would be to remove the check for isLoggedIn and push the user to the Dashboard:
const postLogin = async (e) => {
e.preventDefault()
try {
await login()
props.history.push('/dashboard')
} catch (error) {
dispatch({
type: 'LOGIN_FAILURE',
payload: 'Unable to login'
})
}
}
Then throw an error in your login function when you don't get a 200 back:
const login = async () => {
const loginUser = { status: 200, id: '3654634565659abhdgh' }
if (loginUser.status === 200) {
dispatch({
type: 'LOGIN_SUCCESS',
payload: loginUser.id
})
} else {
throw new Error("Invalid Credentials")
}
}
As your login code is in a try catch block, the user won't get pushed to the Dashboard when the login fails.
Related
I have about 5 separate context api states that I want to rest after logging out from app, since they casing lots of issues any idea how to rest them redirecting doesn't seem to be doing much, and I don't want to refresh, this issue is my log out function will logout the user from server and simply redirect to main page but the state is still in the memory ?
const SignOut = () => {
signOut(auth)
.then(() => {
// Sign-out successful.
})
.catch((error) => {
// An error happened.
const errorCode = error.code;
const errorMessage = error.message;
snackbar.Msg(error, 'A signout error has happened.');
});
history.push('/');
};
I am using react native firebase, if code sent then i want to navigate to otp screen
But it shows an warning :
Non-serializable values were found in the navigation state. Check:
OtpVerification > params.confirm._auth._app._deleteApp (Function)
This can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions'
My code :
In login Screen :
async function signInWithPhoneNumber(phoneNumber) {
try {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
props.navigation.navigate('OtpVerification', {
confirm: confirmation,
phoneNo,
});
} catch (error) {
console.log(error);
}
}
In Otp Screen :
async function confirmCode() {
if (otp) {
try {
await confirm.confirm(otp);
props.navigation.navigate('OnBoarding');
} catch (error) {
AlertMsg('Invalid OTP');
}
} else {
AlertMsg('Please Enter OTP');
}
}
So I believe in belive function where you have confirmation variable, You might have to pass some constant value over there handling the return of firebase signin.
async function signInWithPhoneNumber(phoneNumber) {
try {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
props.navigation.navigate('OtpVerification', {
confirm: confirmation,
phoneNo,
});
} catch (error) {
console.log(error);
}
Check this document - https://reactnavigation.org/docs/troubleshooting/#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state
This expo for your navigation - https://snack.expo.dev/vKpugczaG
I have userDetails in redux store and I used redux persist with redux storage to save the details after loads page or after closing my app.
The problem is, when I logged in and I don't checked the rememberMe in index.js is checked if !rememberMe and do logOut() right after login.
So I need to check just at the entrance into the app for the first time and if !rememberMe I will call logOut() but if userDetails !== 'emepty' && rememberMe I will do login() for get valid token.
Why I need to check this it in the first time? because I need to reset the uesrDetails to guest details if !rememberMe.
This is my index.js:
const setBeforeLists = useCallback(
(lists) => {
beforeListsChanged(lists);
},
[beforeListsChanged]
);
const setUserDetails = useCallback(
(userDetails) => {
userDetailsChanged(userDetails);
},
[userDetailsChanged]
);
useEffect(() => {
//userDetails.id === 'empty' is guest.
if (
userDetails.id !== 'empty' &&
!userDetails.rememberMe
) {
logOut(setUserDetails, setBeforeLists);
}
}, [
setBeforeLists,
setUserDetails,
userDetails
]);
When you refresh the app, the store will be reset. The token is gone. As we expected.
So, we can’t use the state to store the authentication token. This is where AsyncStorage comes onto the stage.
I'm replying to a react-native application.
If it's a web application use session storage.
Let’s import AsyncStorage in the React Native project:
import { AsyncStorage } from "react-native";
Then, let’s create two functions,setToken and getToken, for storing and retrieving the persistent data. These are the main methods of this tutorial. Here, we are storing and retrieving the user data from/to AsyncStorage.
async storeToken(user) {
try {
await AsyncStorage.setItem("userDetails", JSON.stringify(user));
} catch (error) {
console.log("Something went wrong", error);
}
}
async getToken(user) {
try {
let userDetails = await AsyncStorage.getItem("userDetails");
let data = JSON.parse(userDetails);
console.log(data);
} catch (error) {
console.log("Something went wrong", error);
}
}
componentDidMount() {
this.getToken();
}
Let’s prove it. The token is thereafter you quit & reopen the app. Try it out yourself. Now, our app automatically logs you in after each session.
Hope this helps!
With redux-saga and react router 4. I am trying to implement a flow for user registration. I am focusing on the part where user is presented a registration screen on /user/register route.
GOAL
Goal is to update the status of user registration on this same screen as an Alert depending upon either the user created successfully or there already exists a user. I am using redux-saga and using history.push from saga to update the view.
PROBLEM
The alert is shown but only after I reload the /user/register page.
I am passing state to history.push from my saga and then in my component based on that state which I extract from this.props.location.state I prepare the content for alert.
Register Component
// Form submission handler
handleUserRegistration = user => {
this.props.registerUser(user, this.props.history);
}
// Prepring the alert content
getAlertUI = signupState => {
if (signupState.signupSuccess) {
return <UncontrolledAlert color='success'>{'Verification email sent. Please verify your account.'}</UncontrolledAlert>
}else {
return <UncontrolledAlert color='danger'>{signupState.error.message}</UncontrolledAlert>
}
}
render () {
let alertContent = null;
const signupResponse = this.props.location.state;
if (signupResponse) {
if (signupResponse.error) {
alertContent = this.getAlertUI({signupSuccess: false, error: signupResponse.error});
}else {
if (signupResponse.verificationEmailSent) {
alertContent = this.getAlertUI({signupSuccess: true})
}
}
}
return (
<div> {alertContent} </div>
// My form component goes here.
)
}
While is my saga. I am using history.push with the necessary information.
saga.js
const registerWithEmailPasswordAsync = async (userData) =>
await axios.post(apiUrls.SINGUP_USER, userData )
.then(response => {
return {
isError: false,
data: response.data,
}
})
.catch(error => {
return {
isError: true,
errorDetails: {
status: error.response ? error.response.status : null,
message: error.response ? error.response.data : null,
}
}
})
function* registerUser({ payload }) {
const { history } = payload;
try {
const registerUser = yield call(registerWithEmailPasswordAsync, payload.user);
if (!registerUser.isError) {
history.push('/user/register', {verificationEmailSent: true});
} else {
if (registerUser.errorDetails) {
history.push('/user/register', {error: registerUser.errorDetails} );
}
}
} catch (error) {
console.log('register error : ', error)
}
}
I am pretty new to this, Please share if this is the better approach or not? And if it is why isn't it updating my view. Any pointers are highly appreciated.
Don't use history.push just to update state
The only reason I could see you using history.push from within the saga is for sending the user to a dashboard/verification page after a successful sign up. There is no reason to use history.push just to pass some state to the component dynamically. Update your redux state by dispatching an action, not using history.push
You don't necessarily have to update state to notify the user of a sign-up error
You asked if your general approach was the best. Personally, I don't think it is. I wouldn't work that hard. Just use something like react-toastify. You can then just place a ToastContainer in your App component, then call toastify(successMessage/error)
I am using the FBSDK to do the registration in react native. I need the name, last name and email in order to pass it to the registration screen and fill the mentioned fields there automatically. Here is my code in login screen:
async _fbAuth() {
//console.log("that", that);
let { isCancelled } = await LoginManager.logInWithReadPermissions(['public_profile','user_posts', 'email','user_birthday']);
if ( !isCancelled ) {
let data = await AccessToken.getCurrentAccessToken();
let token = data.accessToken.toString();
// await afterLoginComplete(token);
const response = await fetch(
`https://graph.facebook.com/me?fields=id,email,name,first_name,last_name,gender,picture,cover&access_token=${token}`);
let result = await response.json();
this.setState({result});
console.log('result result result ', result);
//navigate to complete the registration.
this.props.navigation.navigate('Reg_1', {name: this.state.result.name, email: this.state.result.email, surname: this.state.result.last_name })
}
else {
console.log('Login incomplete');
}
}
Also I have this button to call the function:
<ButtonFB
onPress={ this._fbAuth}
isLoading={false}
isEnabled={true}
label={I18n.t("Accedi_con_Facebook")}
style={commonStyle.Button}
/>
Everything works fine and the data retrieved well. The only problem is the part of navigation and setSate. My problem is that the 'this' has been lost during the login with the facebook. I got the following warning after doing the login with facebook.
Possible: Unhandled promise rejection (id:0): type error:
this.setState is not a function
I also tried to bind the function of _fbAuth, but it doesn't work. Can you help me to solve this problem. Thanks in advance.
You need to bind the function as
_fbAuth = async () => {}
Since this is not being referenced in _fbAuth function.
For more info checkout this artice