I am making a signup form and I want to navigate a user to the login page when the signup is successful. On the signup page, I want to get the updated state. In the signup
handler, I am not getting updated state (status
) in real-time although the status is updating outside the function.
Signup Component
const Signup = () => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const [password, setPassword] = useState('')
const navigate = useNavigate();
const token = localStorage.getItem("token");
useEffect(() => {
if (token) {
navigate('/home')
}
}, [])
const signupForm = [
{
label: "Name",
InputIcon: FaUserAlt,
width: '100%',
handleInput: (e) => {
setName(e.target.value)
}
},
{
label: "Email",
InputIcon: FaEnvelope,
width: '100%',
handleInput: (e) => {
setEmail(e.target.value)
}
},
{
label: "Phone",
InputIcon: FaPhoneAlt,
width: '100%',
handleInput: (e) => {
setPhone(e.target.value)
}
},
]
const dispatch = useDispatch()
const { status } = useSelector((state) => {
return state.auth;
})
console.log(status);
const signup = async (e) => {
e.preventDefault()
const user = {
name,
email,
phone,
password
}
await dispatch(signupUser(user))
console.log(status);
// if (status == 'success') {
// navigate('/')
// }
}
return (
<div className='form-container'>
<img src={userImage} alt='user' />
<form>
<h2>Sign up</h2>
{
signupForm.map((data, i) => {
return <OutlinedTextField
key={i}
style={{ width: data.width }}
label={data.label}
InputIcon={data.InputIcon}
onChangeFunc={data.handleInput}
/>
})
}
<OutlinedPasswordTextField
style={{ width: '100%' }}
label={'Password'}
InputIcon={FaLock}
onChangeFunc={(e) => setPassword(e.target.value)}
/>
<div className='btn-container'>
<AppButton text={status == 'loading' ? <CircularProgress color='inherit' size="15px" /> : 'Sign up'}
disabled={status == 'loading' ? true : false}
onClickFunc={signup}
style={{ width: '80px' }}
/>
<Link to="/">Login</Link>
</div>
</form>
</div>
)
}
Authentication Slice
const STATUSES = {
SUCCESS: 'success',
LOADING: 'loading',
FAIL: 'fail'
}
export const signupUser = createAsyncThunk('user/signup', async (user) => {
const data = await axios.post('http://localhost:5000/api/signup', user)
.then(res => {
console.log(res.data);
})
.catch(err => {
toast.error(err.response.data.message)
throw new Error(err.response.data.message)
})
console.log(data);
return data
})
const productsSlice = createSlice({
name: 'product',
initialState: {
data: [],
status: STATUSES,
loading: false,
},
extraReducers: (builder) => {
builder.addCase(signupUser.pending, (state, action) => {
console.log(action, state)
state.status = STATUSES.LOADING
})
builder.addCase(signupUser.fulfilled, (state, action) => {
console.log(action, state)
state.status = STATUSES.SUCCESS
state.data = action.payload
})
builder.addCase(signupUser.rejected, (state, action) => {
console.log(action, state)
state.status = STATUSES.FAIL
})
}
})
2
Answers
You should check the status inside the Signup with useSelector. If your status changes you can re-trigger your navigate inside the useEffect with the dependency array.
The main issue is that the selected
status
state is closed over in callback scope and will not ever update during the life of thesignup
callback. In other words, it is a stale closure over thestatus
value.A secondary issue I see in the
signupUser
action is not returning the fetched data correctly; nothing is returned from the.then
-able that logsres.data
.You are using an asynchronous Thunk action which returns a Promise. You can await the dispatched
signupUser
to resolve. Use the.unwrap
function to check if the asynchronous action was successful or not. See Checking Errors after Dispatching and Handling Thunk Errors for details.