Reputation: 3023
I am working on an authentication system. Here when user enter wrong password i want to show toast but the problem it the way i am doing its not working.
I am using react-redux for state management In my app.
Here is how my login flow works
Here when login button is pressed it dispatches getLogin
action, if it gets error from server side it sends loginError which is a string. That's why i am check it here. LoginError
is coming from redux store and loginSuccess
as well.
Login.tsx
const { width: wWidth, height: wHeight } = Dimensions.get("window");
const validationSchema = Yup.object().shape({
password: Yup.string().required(),
email: Yup.string().email().required(),
});
const Login = ({ navigation, getLogin, authentication }: LoginProps) => {
const { toast } = useToast();
const { loginLoading, loginSuccess, loginError } = authentication;
const {
handleChange,
handleBlur,
handleSubmit,
values,
errors,
touched,
setFieldValue,
} = useFormik({
validationSchema,
initialValues: {
password: "",
email: "",
callback: false,
},
onSubmit: async (values) => {
getLogin(values.email, values.password, navigation);
},
});
if (loginError !== "" && loginSuccess === "") {
console.log("HelloFromIf");
toast({
message: "Wrong username or password!",
bg: "background",
color: "text",
accentColor: "main",
iconFamily: "Feather",
iconName: "alert-triangle",
iconColor: "error",
});
}
return (
<Box
backgroundColor="primaryText"
flex={1}
justifyContent="center"
alignItems="center"
>
<Box
justifyContent="center"
alignItems="center"
height={wHeight / 2}
width={wWidth}
flex={1}
>
<Box style={{ marginTop: 100 }} height={wHeight / 3} width={wWidth / 2}>
<Image
style={{ height: "100%", width: "100%" }}
source={require("../../../assets/login-logo.png")}
/>
</Box>
</Box>
<Box
borderTopLeftRadius="l"
borderTopRightRadius="l"
width={wWidth}
backgroundColor="iconBackground"
flex={1}
>
<Box marginHorizontal="l">
<Box>
<TextField
onChangeText={handleChange("email")}
placeholder="email"
onBlur={handleBlur("email")}
error={errors.email}
touched={touched.email}
/>
</Box>
<Box>
<TextField
secureTextEntry={true}
placeholder="Password"
onChangeText={handleChange("password")}
onBlur={handleBlur("password")}
error={errors.password}
touched={touched.password}
/>
</Box>
<LargeButton
loading={loginLoading}
onPress={handleSubmit}
label={"LOGIN"}
/>
<Box paddingVertical="m" alignItems="center">
<TouchableWithoutFeedback>
<Text variant="seeAll">Forgot Password ?</Text>
</TouchableWithoutFeedback>
</Box>
<Box paddingVertical="m" alignItems="center">
<TouchableWithoutFeedback
onPress={() => navigation.navigate("SignUp")}
>
<Text variant="seeAll">Create new account!</Text>
</TouchableWithoutFeedback>
</Box>
</Box>
</Box>
</Box>
);
};
function mapStateToProps(state: any) {
return {
authentication: state.auth,
};
}
const mapDispatchToProps = (dispatch: any) => {
return {
getLogin: (email: string, password: string, navigation: any) =>
dispatch(userLogin(email, password, navigation)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);
authAction.tsx
export const userLogin = (email: string, password: string, navigation: any) => (
dispatch: any
) => {
dispatch({
type: LOGIN_USER_LOADING,
});
const base64 = require("base-64");
const hash = "Basic " + base64.encode("karthik:karthik");
let data = new FormData();
data.append("username", email);
data.append("password", password);
data.append("grant_type", "password");
let config: AxiosRequestConfig = {
method: "post",
url: _login_user,
headers: {
Authorization: hash,
},
data: data,
};
axios(config)
.then((res: AxiosResponse) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: res.data,
});
const _rest = new restServices();
_rest.saveToken(res.data);
navigation.navigate("Home");
})
.catch((err) => {
dispatch({
type: LOGIN_USER_ERROR,
error: err,
});
});
};
authReducer.tsx
import {
LOGIN_USER_LOADING,
USER_SIGN_UP_ERROR,
USER_SIGN_UP_LOADING,
} from "./../actions/constants/authConstant";
import {
LOGIN_USER_SUCCESS,
LOGIN_USER_ERROR,
LOGOUT_USER,
} from "../actions/constants/authConstant";
import { USER_AUTHORIZED } from "../actions/constants/authConstant";
import { USER_SIGN_UP } from "../actions/constants/authConstant";
export type AuthState = {
readonly isAuthenticated: boolean;
readonly userData: {};
readonly loginLoading: boolean;
readonly loginSuccess: string;
readonly loginError: string;
};
const initialState: AuthState = {
isAuthenticated: false,
userData: {},
loginLoading: false,
loginSuccess: "",
loginError: "",
};
const authReducer = (state = initialState, action: any) => {
switch (action.type) {
case LOGIN_USER_LOADING:
return {
...state,
loginLoading: true,
isAuthenticated: false,
loginError: "",
loginSuccess: "",
};
case LOGIN_USER_SUCCESS:
return {
...state,
isAuthenticated: true,
loginLoading: false,
userData: action.payload,
loginError: "",
loginSuccess: "User Logged In.",
};
case LOGIN_USER_ERROR:
return {
...state,
loginLoading: false,
loginError: action.error,
loginSuccess: "",
};
default:
return {
...state,
};
}
};
export default authReducer;
I am not sure weather i am doing it right or wrong please help me solve this issue. This toast show even when login is successfull.
Upvotes: 0
Views: 2174
Reputation: 2478
You should put that condition before render Class component
import {useSelector, useDispatch} from 'react-redux'
const { width: wWidth, height: wHeight } = Dimensions.get("window");
const validationSchema = Yup.object().shape({
password: Yup.string().required(),
email: Yup.string().email().required(),
});
const Login = ({ navigation, getLogin, authentication }: LoginProps) => {
const { toast } = useToast();
const authentication = useSelector(state => state.auth);
const { loginLoading, loginSuccess, loginError } = authentication;
const dispatch = useDispatch();
const {
handleChange,
handleBlur,
handleSubmit,
values,
errors,
touched,
setFieldValue,
} = useFormik({
validationSchema,
initialValues: {
password: "",
email: "",
callback: false,
},
onSubmit: async (values) => {
dispatch(userLogin(email, password, navigation)) // You call directly your action
},
});
if (loginError !== "" && loginSuccess === "") {
console.log("HelloFromIf");
toast({
message: "Wrong username or password!",
bg: "background",
color: "text",
accentColor: "main",
iconFamily: "Feather",
iconName: "alert-triangle",
iconColor: "error",
});
}
return (
<Box
backgroundColor="primaryText"
flex={1}
justifyContent="center"
alignItems="center"
>
<Box
justifyContent="center"
alignItems="center"
height={wHeight / 2}
width={wWidth}
flex={1}
>
<Box style={{ marginTop: 100 }} height={wHeight / 3} width={wWidth / 2}>
<Image
style={{ height: "100%", width: "100%" }}
source={require("../../../assets/login-logo.png")}
/>
</Box>
</Box>
<Box
borderTopLeftRadius="l"
borderTopRightRadius="l"
width={wWidth}
backgroundColor="iconBackground"
flex={1}
>
<Box marginHorizontal="l">
<Box>
<TextField
onChangeText={handleChange("email")}
placeholder="email"
onBlur={handleBlur("email")}
error={errors.email}
touched={touched.email}
/>
</Box>
<Box>
<TextField
secureTextEntry={true}
placeholder="Password"
onChangeText={handleChange("password")}
onBlur={handleBlur("password")}
error={errors.password}
touched={touched.password}
/>
</Box>
<LargeButton
loading={loginLoading}
onPress={handleSubmit}
label={"LOGIN"}
/>
<Box paddingVertical="m" alignItems="center">
<TouchableWithoutFeedback>
<Text variant="seeAll">Forgot Password ?</Text>
</TouchableWithoutFeedback>
</Box>
<Box paddingVertical="m" alignItems="center">
<TouchableWithoutFeedback
onPress={() => navigation.navigate("SignUp")}
>
<Text variant="seeAll">Create new account!</Text>
</TouchableWithoutFeedback>
</Box>
</Box>
</Box>
</Box>
);
};
export default Login;
Upvotes: 0
Reputation: 3683
You need to use useEffect
to perform a side effect when your state updates.
You can replace you current code with this:
useEffect(() => {
if (loginError !== "" && loginSuccess === "") {
console.log("HelloFromIf");
toast({
message: "Wrong username or password!",
bg: "background",
color: "text",
accentColor: "main",
iconFamily: "Feather",
iconName: "alert-triangle",
iconColor: "error",
});
}
}, [loginError, loginSuccess]);
The function will run every time either loginError or loginSuccess changes, and display the toast whenever the conditions are met.
By the way, you should probably have a look at this to improve your authentication flow: https://reactnavigation.org/docs/auth-flow
Upvotes: 1