I am trying to redirect user after successful login but changing state in saga is causing an infinite loop.
In my component I am using useSelector to listen to changes in isValidSession from store. If isValidSession changes to true, I want to redirect user to another page. However, when isValidSession is changed, my component keeps re-rendering in an infinite loop.
component
import { Link, useLocation } from 'react-router-dom'
import { Redirect } from 'react-router'
import MainLayout from '../../../layouts/MainLayout'
import { Typography, Alert, Row, Col, Divider } from 'antd'
import LoginForm from './LoginForm'
import { useDispatch, useSelector } from 'react-redux'
import * as loginActions from './Login.action'
const { Title } = Typography
export default function Login() {
const dispatch = useDispatch()
const { isValidSession, error } = useSelector((state) => state.login)
const location = useLocation()
let { from } = location.state || { from: { pathname: '/templates' } }
const handleLogin = ({ email, password }) =>
{
dispatch(loginActions.authenticate({ email, password }))
}
const getAPIError = (error) =>
error.status == 401
? 'Invalid username or password'
: 'Something went wrong'
if (isValidSession) return <Redirect push to={from.pathname} />
return (
<MainLayout>
<Row align='stretch' className='vh-100'>
<Col xs={24} md={12} lg={10} xl={8} xxl={6} className='bg--white'>
<div className='d-flex align-items-center h-100 p-5'>
<div id='login' className='w-100'>
<Title level={3}>Welcome Back!</Title>
{/* ERROR MESSAGE */}
{error ? (
<Alert
message={getAPIError(error)}
showIcon
type='error'
className='mb-2'
/>
) : (
''
)}
{/* FORM */}
<LoginForm onFinish={handleLogin} />
<Divider />
{/* ADDITIONAL LINKS */}
<div className='text-center'>
<p>
Don't have an account yet?{' '}
<Link
to={{ pathname: '/register', state: { from: location } }}
>
Sign up
</Link>
</p>
<Link
to={{
pathname: '/auth/recover-password',
state: { from: location },
}}
>
Forgot your password?
</Link>
</div>
</div>
</div>
</Col>
</Row>
</MainLayout>
)
}
In my reducer, for action types.LOGIN_SUCCESS, I am making isValidSession to true.
reducer
const loginReducer = (state = initialState, action) => {
switch (action.type) {
case types.LOGIN_REQUESTING: {
return {
...state,
submitted: false,
}
}
case types.LOGIN_SUCCESS: {
return {
...state,
submitted: true,
successful: true,
isValidSession: true,
}
}
case types.LOGIN_ERROR: {
const { error } = action
return {
...state,
error,
submitted: false,
successful: false,
isValidSession: false,
}
}
case types.UPDATE_LOGIN_CREDENTIAL: {
const { login } = action
return {
...state,
login: {
...state.login,
...login,
},
}
}
case types.IS_LOGGINGIN: {
const { isLoggingIn } = action
return {
...state,
isLoggingIn,
}
}
default:
return {
...state,
}
}
}
In my saga code, you can see that I am doing yield put({ type: types.LOGIN_SUCCESS }) to change isValidSession to true.
saga
function* authenticateSage(payload) {
try {
alert('authenticateSage')
const response = yield call(authService.login, payload.login)
const { profile, account } = response
yield put({ type: types.LOGIN_SUCCESS })
} catch (error) {
yield put({
type: types.LOGIN_ERROR,
error: { status: error.status, message: error.data?.message },
})
}
}
export default function* watchAuthentication() {
yield takeLatest(types.LOGIN_REQUESTING, authenticateSage)
}