Home > Blockchain >  How to display error message on the mern app
How to display error message on the mern app

Time:10-15

I'm trying to display an error message when a user tries to sign in with an unregistered user but I'm not able to get that error message on the frontend(reactjs) that I am sending from my backend(nodejs, express, MongoDB).

I am using redux to handle the state of the react app.

user login form onSubmit code:

   const onSubmit = (data) => {
    if (isLogin) {
      dispatch(signin(data, history));
    } else {
      dispatch(signup(data, history));
    }
  };

actions creators for auth

import * as api from "../api";

export const signin = (formData, history) => async (dispatch) => {
  try {
    // login the user
    const { data } = await api.signIn(formData);
    dispatch({ type: "AUTH", data });
    history.push("/");
  } catch (error) {
    console.log(error);
    dispatch({ type: "ERROR", data: error.response.data.message });
  }
};

export const signup = (formData, history) => async (dispatch) => {
  try {
    // sign up the user
    const { data } = await api.signUp(formData);
    dispatch({ type: "AUTH", data });
    history.push("/");
  } catch (error) {
    console.log(error);
  }
};

reducers for auth:

const authReducer = (state = { authData: null }, action) => {
  switch (action.type) {
    case "AUTH":
      localStorage.setItem("profile", JSON.stringify({ ...action?.data }));
      return { state, authData: action?.data };
    case "LOGOUT":
      localStorage.clear();
      return { state, authData: null };
    case "ERROR":
      return { state, authData: action?.data };
    default:
      return state;
  }
};

export default authReducer;

Backend sign in code

const signin = async (req, res) => {
  const { email, password } = req.body;
  try {
    const existingUser = await User.findOne({ email });
    if (!existingUser)
      return res.status(404).json({ message: "User doesn't exist." });

    const isPasswordCorrect = await bcrypt.compare(
      password,
      existingUser.password
    );
    if (!isPasswordCorrect)
      res.status(404).json({ message: "Invalid Credentials" });

    const token = jwt.sign(
      { email: existingUser.email, id: existingUser._id },
      "test",
      { expiresIn: "1h" }
    );
    res.status(200).json({ result: existingUser, token });
  } catch (error) {
    res.status(500).json({ message: "Something went wrong" });
  }
};

sign API

import axios from "axios";

const API = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
});

export const signIn = (formData) => API.post("/user/signin", formData);
export const signUp = (formData) => API.post("/user/signup", formData);

Anyone, please help me with this.

Screenshots of the error response in Postman: enter image description here

enter image description here

CodePudding user response:

As far as I understood, everything is working fine, and actually, there is no error happening on the frontend side. You have to check the status code of "await api.signIn(formData);" response. If it is 200, it means that everything is ok otherwise you have to check the message in the response to get the message error. As on the frontend there is thrown error to be catched.

CodePudding user response:

I just debugged the issue. First install redux-devtools-extension by npm install --save redux-devtools-extension Then apply this in store(index.js file in your case) as follows

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import App from "./App";
import { reducers } from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";

const middleware = [thunk];

const store = createStore(
  reducers,
  composeWithDevTools(applyMiddleware(...middleware))
);

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

And now you can use the error message anywhere as follows (your Auth.js file)

import React, { useState } from "react";
import { useSelector } from "react-redux";
import Input from "../utils/Input";
import Label from "../utils/Label";
import { useForm, FormProvider } from "react-hook-form";
import { GoogleLogin } from "react-google-login";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router";
import { signin, signup } from "../../actions/auth";

const Auth = () => {
  const [isLogin, setIsLogin] = useState(true);
  const formMethods = useForm();
  const auth = useSelector((state) => state.auth);
  const { authData } = auth;
  const { handleSubmit } = formMethods;

  console.log(authData);
  const dispatch = useDispatch();
  const history = useHistory();

  const onSubmit = (data) => {
    if (isLogin) {
      dispatch(signin(data, history));
    } else {
      dispatch(signup(data, history));
    }
  };

  const googleSuccess = async (res) => {
    const result = res?.profileObj;
    const token = res?.tokenId;

    try {
      dispatch({ type: "AUTH", data: { result, token } });
      history.push("/");
    } catch (error) {
      console.log(error);
    }
  };

  const googleFailure = (error) => {
    console.log(error);
    console.log("Google sign in was unsuccessful");
  };

  return (
    <section className="col-start-1 col-end-2 md:col-start-2 md:col-end-3 row-start-2 row-end-3 md:row-start-1 md:row-end-2 mx-3 sm:mx-0 md:my-auto">
      <div className=" w-full max-w-md bg-primaryOne px-6 py-8 rounded-md shadow-md mx-auto">
        <FormProvider {...formMethods}>
          <form className="" onSubmit={handleSubmit(onSubmit)}>
            <div className="w-full flex justify-around mb-2">
              <button
                className={`${
                  isLogin
                    ? "bg-secondaryTwo"
                    : "transition bg-transparent hover:bg-secondaryTwo"
                } text-white text-xs font-bold px-6 py-4 rounded-full`}
                type="button"
                onClick={() => setIsLogin(true)}
              >
                LOG IN
              </button>
              <button
                className={`${
                  !isLogin
                    ? "bg-secondaryTwo"
                    : "transition bg-transparent hover:bg-secondaryTwo"
                } text-white text-xs font-bold px-6 py-4 rounded-full`}
                type="button"
                onClick={() => setIsLogin(false)}
              >
                SIGN UP
              </button>
            </div>
            <div>
              {!isLogin && (
                <div>
                  <Label labelName="Name" />
                  <Input inputName="name" type="text" bgColor="primaryTwo" />
                </div>
              )}
              <div>
                <Label labelName="Email" />
                <Input inputName="email" type="email" bgColor="primaryTwo" />
              </div>
              <div>
                <Label labelName="Password" />
                <Input
                  inputName="password"
                  type="password"
                  bgColor="primaryTwo"
                />
              </div>
            </div>
            <div className="text-center">
              <button
                type="button"
                onClick={() => setIsLogin(!isLogin)}
                className="text-neutral font-extralight text-xs pt-6"
              >
                {authData && <h1 style={{ color: "red" }}>{authData}</h1>}
                {!isLogin
                  ? "Already have an account? Log In"
                  : "Don't have an account? Sign Up"}
              </button>
            </div>

            <button className="bg-secondaryTwo hover:bg-secondaryOne transition px-4 py-3 w-full rounded-md text-white font-bold mt-4 shadow-md">
              {isLogin ? "Log In" : "Sign Up"}
            </button>
            <div className="flex items-center py-6">
              <div className="w-1/2 h-px bg-white bg-opacity-40"></div>
              <p className="text-white px-1 text-xs">OR</p>
              <div className="w-1/2 h-px bg-white bg-opacity-40"></div>
            </div>
            <div>
              <GoogleLogin
                clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
                onSuccess={googleSuccess}
                onFailure={googleFailure}
                cookiePolicy="single_host_origin"
                render={(renderProps) => (
                  <button
                    className="bg-blue-600 hover:bg-blue-500 transition px-4 py-3 w-full rounded-md text-white font-bold mb-4 shadow-md"
                    type="button"
                    onClick={renderProps.onClick}
                    disabled={renderProps.disabled}
                  >
                    <i className="fab fa-google mr-2"></i>Continue with Google
                  </button>
                )}
              />
            </div>
          </form>
        </FormProvider>
      </div>
    </section>
  );
};

export default Auth;
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

And one last thing. Check nested object before using its properties like error.response.data.message (your auth.js actions file)

import * as api from "../api";

export const signin = (formData, history) => async (dispatch) => {
  try {
    // login the user
    const { data } = await api.signIn(formData);
    dispatch({ type: "AUTH", data });
    history.push("/");
  } catch (error) {
    console.log("An error occured while ");
    const errMsg =
      error.response && error.response.data.message
        ? error.response.data.message
        : error.message;
    dispatch({ type: "ERROR", data: errMsg });
    console.log(errMsg);
  }
};

export const signup = (formData, history) => async (dispatch) => {
  try {
    // sign up the user
    const { data } = await api.signUp(formData);
    dispatch({ type: "AUTH", data });
    history.push("/");
  } catch (error) {
    console.log(error);
  }
};
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Hope it helps!

  • Related