I have a functional login page connected with redux, I'm firing an async event onSubmit that will trigger the emailLogin
action, I am using useEffect
to detect the change of the isLoading
prop to see whether login finished or not. If login success, the redux store should have the user object, if failed, the user should remain null.
The question is, I know that the login is success, which should triggered the change of isLoading
, the parameter that decide whether the useEffect
, however, the useEffect
is not fired. Also, the console.log('done');
after the line await emailLogin(authData);
is never fired. Ssomething is wrong.
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { emailLogin } from '../actions/index';
function Login({ user, isLoading, emailLogin }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const history = useHistory();
useEffect(() => {
console.log('useEffect fired', user, isLoading); //<-----This does not fire after login success
if (user) {
history.push('/protected_home');
}
}, [isLoading]);
const submitEmailLoginForm = async (e) => {
e.preventDefault();
const authData = { email, password };
await emailLogin(authData);
console.log('done'); // <------- This is never fired
};
return (
<div>
<h2>Login</h2>
<Link to="/">back</Link>
<form onSubmit={submitEmailLoginForm}>
<label>
email:
<input
type="text"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
password:
<input
type="text"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
const mapStateToProps = (state) => ({
user: state.user,
isLoading: state.isLoading
});
const mapDispatch = {
emailLogin: emailLogin
};
export default connect(mapStateToProps, mapDispatch)(Login);
My action file:
import axios from 'axios';
export const authActions = {
EMAIL_LOGIN_START: '@@EMAIL_LOGIN_START',
EMAIL_LOGIN_SUCCESS: '@@EMAIL_LOGIN_SUCCESS'
};
export const emailLogin = ({ email, password }) => async (dispatch) => {
dispatch({ type: authActions.EMAIL_LOGIN_START });
try {
const response = await axios.post('http://localhost:5001/api/auth', {
email: email,
password: password
});
dispatch({
type: authActions.EMAIL_LOGIN_SUCCESS,
payload: {
user: { ...response.data }
}
});
} catch (error) {
console.log('Should dispatch api error', error.response);
}
};
My Reducer:
import { authActions } from '../actions/index';
const initialState = {
user: null,
isLoading: false
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case authActions.EMAIL_LOGIN_START:
return { ...state, isLoading: true };
case authActions.EMAIL_LOGIN_SUCCESS:
console.log('Reducer check => Login is success'); //<-----this line is printed
return { ...state, user: action.payload.user, isLoading: false };
default:
return state;
}
};
export default userReducer;
In the reducer, I see that the success action is actually triggered by checking the console.log()
. Also in the redux dev tool, I can actually see that the login is success and the isLoading
prop has changed :
This solve my problem
const mapStateToProps = (state) => ({
user: state.userReducer.user,
isLoading: state.userReducer.isLoading
});