aditya kumar
aditya kumar

Reputation: 3023

How to show toast just after dispatching a successful action in react native

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

Answers (2)

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

Valentin Briand
Valentin Briand

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

Related Questions