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;
Remove useEffect from Login component and update Auth function like
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