How to redirect on dashboard after successfully login in react js using redux?

1.1k views Asked by At

I have created login page in react js and api in node js. I am trying to login using email and password using redux.

Login Page

    const Login = () => {

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const dispatch = useDispatch();
  const navigate = useNavigate();
  const { user, isError, isSuccess, isLoading, message } = useSelector(
    (state) => state.auth
  );

  useEffect(() => {
    if (user || isSuccess) {
      navigate("/dashboard");
    }
    dispatch(reset());
  }, [user, isSuccess, dispatch, navigate]);

  const Auth = (e) => {
    e.preventDefault();
    dispatch(LoginUser({ email, password }));
  };

  return (
    <div className="wrapper">
        <main className="authentication-content">
        <div className="container-fluid">
        <div className="authentication-card">
            <div className="card shadow rounded-0 overflow-hidden">
            <div className="row g-0">
                <div className="col-lg-6 bg-login d-flex align-items-center justify-content-center">
                <img src="assets/images/error/login-img.jpg" className="img-fluid" alt=""/>
                </div>
                <div className="col-lg-6">
                <div className="card-body p-4 p-sm-5">
                    <h5 className="card-title">Sign In</h5>
                    <p className="card-text mb-5">See your growth and get consulting support!</p>
                    <form onSubmit={Auth} className="form-body">
                    {isError && <p className="has-text-centered">{message}</p>}
                        <div className="row g-3">
                        <div className="col-12">
                            <label htmlFor="inputEmailAddress" className="form-label">Email Address</label>
                            <div className="ms-auto position-relative">
                            <div className="position-absolute top-50 translate-middle-y search-icon px-3"><i className="bi bi-envelope-fill"></i></div>
                            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} className="form-control radius-30 ps-5" id="inputEmailAddress" placeholder="Email Address"/>
                            </div>
                        </div>
                        <div className="col-12">
                            <label htmlFor="inputChoosePassword" className="form-label">Enter Password</label>
                            <div className="ms-auto position-relative">
                            <div className="position-absolute top-50 translate-middle-y search-icon px-3"><i className="bi bi-lock-fill"></i></div>
                            <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} className="form-control radius-30 ps-5" id="inputChoosePassword" placeholder="Enter Password"/>
                            </div>
                        </div>
                        <div className="col-6">
                            <div className="form-check form-switch">
                            <input className="form-check-input" type="checkbox" id="flexSwitchCheckChecked" />
                            <label className="form-check-label" htmlFor="flexSwitchCheckChecked">Remember Me</label>
                            </div>
                        </div>
                        <div className="col-6 text-end">    <a href="authentication-forgot-password.html">Forgot Password ?</a>
                        </div>
                        <div className="col-12">
                            <div className="d-grid">
                            <button type="submit" className="btn btn-primary radius-30">{isLoading ? "Loading..." : "Sign In"}</button>
                            </div>
                        </div>
                        </div>
                    </form>
                </div>
                </div>
            </div>
            </div>
        </div>
        </div>
        </main>
    </div>
  )
}

export default Login;

It's showing null and false in user and isSuccess at the time of render the login page. when i click on Sign InButton. It's showing dashboard for one second and again redirect to login page and showing null in user and isSuccess. when i do click 5 to 10 times then once it showing response and hold on dashboard.

AuthSlice file

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";

const initialState = {
    user: null,
    isError: false,
    isSuccess: false,
    isLoading: false,
    message: ""
}

export const LoginUser = createAsyncThunk("user/LoginUser", async(user, thunkAPI) => {
    try {
        const response = await axios.post('http://localhost:5001/login', {
            email: user.email,
            password: user.password
        });
        return response.data;
    } catch (error) {
        if(error.response){
            const message = error.response.data.msg;
            return thunkAPI.rejectWithValue(message);
        }
    }
});

export const getMe = createAsyncThunk("user/getMe", async(_, thunkAPI) => {
    try {
        const response = await axios.get('http://localhost:5001/me');
        return response.data;
    } catch (error) {
        if(error.response){
            const message = error.response.data.msg;
            return thunkAPI.rejectWithValue(message);
        }
    }
});

export const LogOut = createAsyncThunk("user/LogOut", async() => {
    await axios.delete('http://localhost:5001/logout');
});

export const authSlice = createSlice({
    name: "auth",
    initialState,
    reducers:{
        reset: (state) => initialState
    },
    extraReducers:(builder) =>{
        builder.addCase(LoginUser.pending, (state) =>{
            state.isLoading = true;
        });
        builder.addCase(LoginUser.fulfilled, (state, action) =>{
            state.isLoading = false;
            state.isSuccess = true;
            state.user = action.payload;
        });
        builder.addCase(LoginUser.rejected, (state, action) =>{
            state.isLoading = false;
            state.isError = true;
            state.message = action.payload;
        })

        // Get User Login
        builder.addCase(getMe.pending, (state) =>{
            state.isLoading = true;
        });
        builder.addCase(getMe.fulfilled, (state, action) =>{
            state.isLoading = false;
            state.isSuccess = true;
            state.user = action.payload;
        });
        builder.addCase(getMe.rejected, (state, action) =>{
            state.isLoading = false;
            state.isError = true;
            state.message = action.payload;
        })
    }
});

export const {reset} = authSlice.actions;
export default authSlice.reducer;

Dashboard Page

const Dashboard = () => {

  const dispatch = useDispatch();
  const navigate = useNavigate();
  const { isError } = useSelector((state) => state.auth);

  useEffect(() => {
    dispatch(getMe());
  }, [dispatch]);

  useEffect(() => {
    if (isError) {
      navigate("/");
    }
  }, [isError, navigate]);
  return (
    <Layout>
      <Welcome />
    </Layout>
  );
};

export default Dashboard;
1

There are 1 answers

1
Arifur Rahaman On

Remove useEffect from Login component and update Auth function like

const Auth = (e) => {
    e.preventDefault();
    dispatch(LoginUser({ email, password }))
    .unwrap() // <-- async Thunk returns a promise, that can be 'unwrapped')
    .then(() => navigate("/dashboard"))
  };

Explaination: When using createAsyncThunk API, the returned function is promisified by default. You can wait for the promise resolution using unwrap() method (inpired by Rust, I guess…). When the promise is resolved, it can be further chained with then() calls, so you can navigate from there.

Refference: useNavigate blocks rest of the content in createAsyncThunk, redux toolkit