I'm working simple reactjs login form with redux/toolkit. I wanted to redirect to dashboard
page after successful login. It's throwing following error. I'm new to reactjs and please let me know if I missed anything.
Error:
Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
authSlice.js
import { useNavigate } from 'react-router-dom';
export const submitLogin =
({
email,
password
}) =>
async dispatch => {
const history = useNavigate();
return jwtService
.signInWithEmailAndPassword(email, password)
.then(user => {
history('/dashboard');
return dispatch(loginSuccess());
})
.catch(error => {
return dispatch(loginError(error));
});
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
loginSuccess: ....
loginError: ....
logoutSuccess: ....
},
extraReducers: {},
});
export const { loginSuccess, loginError, logoutSuccess } = authSlice.actions;
export default authSlice.reducer;
Login.js
const Login = () => {
function handleSubmit(model) {
dispatch(submitLogin(model));
}
return (
<Formsy onValidSubmit={handleSubmit} ref={formRef}>
<input type="text" placeholder="username" />
....
</Formsy>
)
}
App.js
<Routes>
<Route path="/dashboard" element={<ProtectedRoutes />}>
<Route path="" element={<Dashboard />} />
</Route>
<Route exact path="/" element={<Login />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
import React from 'react';
import { useSelector } from 'react-redux';
import { Navigate, Outlet } from 'react-router-dom';
const useAuth = () => {
const auth = useSelector(({ auth }) => auth);
return auth && auth.loggedIn;
};
const ProtectedRoutes = () => {
const isAuth = useAuth();
return isAuth ? <Outlet /> : <Navigate to="/" />
}
export default ProtectedRoutes;
CodePudding user response:
Issue
You are attempting to use the useNavigate
hook outside a React component, which is an invalid use. React hooks must be called at the top-level, they cannot be called conditionally, in functions, loops, etc...
Solutions
Pass the navigate function to the submitLogin
action creator.
export const submitLogin = ({ email, password }, navigate) =>
async dispatch => {
return jwtService
.signInWithEmailAndPassword(email, password)
.then(user => {
navigate('/dashboard');
return dispatch(loginSuccess());
})
.catch(error => {
return dispatch(loginError(error));
});
};
...
const Login = () => {
const navigate = useNavigate();
function handleSubmit(model) {
dispatch(submitLogin(model, navigate));
}
return (
<Formsy onValidSubmit={handleSubmit} ref={formRef}>
<input type="text" placeholder="username" />
....
</Formsy>
);
}
Chain/await
the returned Promise
export const submitLogin = ({ email, password }) =>
async dispatch => {
return jwtService
.signInWithEmailAndPassword(email, password)
.then(user => {
dispatch(loginSuccess());
return user;
})
.catch(error => {
dispatch(loginError(error));
throw error;
});
};
...
const Login = () => {
const navigate = useNavigate();
async function handleSubmit(model) {
try {
await dispatch(submitLogin(model));
navigate('/dashboard');
} catch(error) {
// handle error, log, etc...
}
}
return (
<Formsy onValidSubmit={handleSubmit} ref={formRef}>
<input type="text" placeholder="username" />
....
</Formsy>
);
}
CodePudding user response:
When you use react hooks , there are some rules you can't bypass them
- You have to call useHook() at the root level of your component or
function - You can't make conditional useHook() call
I think but maybe i am wrong that error occurred in this part of your code
export const submitLogin =
({
email,
password
}) =>
async dispatch => {
const history = useNavigate(); // x Wrong you can't call inside another function
...
};