For some reason my Login component is not redirecting a successful login to the right page. I am using a Spring boot backend with a React frontend and am pretty sure I can do this with react on the frontend by using history.push('/profile')
to allow a successful login to be redirected to the /profile page but for some reason it stays on /login even after successfully logging in. Any ideas what I am doing wrong? Thank you!
Login.jsx:
import React, {useState, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {Link} from 'react-router-dom';
import {Formik, Field, Form, ErrorMessage} from 'formik';
import * as Yup from 'yup';
import {login} from '../../slices/auth';
import {clearMessage} from '../../slices/messages';
const Login = (props) => {
const [loading, setLoading] = useState(false);
const {isLoggedIn} = useSelector((state) => state.auth);
const {message} = useSelector((state) => state.message);
const dispatch = useDispatch();
useEffect(() => {
dispatch(clearMessage());
}, [dispatch]);
const initialValues = {
username: '',
password: '',
};
const validationSchema = Yup.object().shape({
username: Yup.string().required('Please enter your username'),
password: Yup.string().required('Please enter your password'),
});
const handleLogin = (formValue) => {
const {username, password} = formValue;
setLoading(true);
dispatch(login({username, password}))
.unwrap()
.then(() => {
props.history.push('/profile');
window.location.reload();
})
.catch(() => {
setLoading(false);
});
};
if (isLoggedIn) {
return <Link to='/profile' />;
}
return (
<div className='login'>
<div className='card card-container'>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLogin}
>
<Form>
<div className='form-group'>
<Field
name='username'
type='text'
className='form-control'
placeholder='Username'
/>
<ErrorMessage
name='username'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<Field
name='password'
type='password'
className='form-control'
placeholder='Password'
/>
<ErrorMessage
name='password'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary btn-block'
disabled={loading}
>
{loading && (
<span className='spinner-border spinner-border-sm'></span>
)}
<span>Login</span>
</button>
</div>
</Form>
</Formik>
</div>
{message && (
<div className='form-group'>
<div className='alert alert-danger' role='alert'>
{message}
</div>
</div>
)}
</div>
);
};
export default Login;
auth.js:
// We’re gonna import AuthService to make asynchronous HTTP requests with trigger one or more dispatch in the result.
// – register(): calls the AuthService.register(username, email, password) & dispatch setMessage if successful/failed
// – login(): calls the AuthService.login(username, password) & dispatch setMessage if successful/failed
// – logout(): calls the AuthService.logout().
// setMessage is imported from message slice that we’ve created above.
// We also need to use Redux Toolkit createAsyncThunk which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise.
//There are 3 async Thunks to be exported:
// register
// login
// logout
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
messages.js:
// This updates message state when message action is dispatched from anywhere in the application. It exports 2 action creators:
// setMessage
// clearMessage
import {createSlice} from '@reduxjs/toolkit';
const initialState = {};
const messageSlice = createSlice({
name: 'message',
initialState,
reducers: {
setMessage: (state, action) => {
return {message: action.payload};
},
clearMessage: () => {
return {message: ''};
},
},
});
const {reducer, actions} = messageSlice;
export const {setMessage, clearMessage} = actions;
export default reducer;
CodePudding user response:
Remove the window.location.reload();
from the handleLogin
function, it is reloading your app and killing the navigation action.
Since it appears you are using react-router-dom
v6, there are no route props and there is no history
object. Instead, there is a useNavigate
hook.
import { useNavigate } from 'react-router-dom';
...
const Login = (props) => {
const navigate = useNavigate();
...
const handleLogin = (formValue) => {
const { username, password } = formValue;
setLoading(true);
dispatch(login({ username, password }))
.unwrap()
.then(() => {
navigate('/profile', { replace: true });
})
.catch(() => {
setLoading(false);
});
};
...