I am trying to redirect user after successful login. I did it this way but it does not work. How can I fix this issue or how can I do it correctly?
main.jsx
Login.jsx
imports ...
function Login() {
const [login, setLogin] = useState('');
const [password, setPassword] = useState('');
const [loginClass, setLoginClass] = useState('');
const [passwordClass, setPasswordClass] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false);
const submit = async (evt) => {
evt.preventDefault();
console.log(login, password);
if (login.length === 0 || password.length === 0) {
console.log('stop')
if (!login) {
setLoginClass('is-invalid');
}
if (!password) {
setPasswordClass('is-invalid');
}
} else {
console.log('req')
await axios
.post('http://localhost:5000/test/auth/login', {
login: login,
password: password
})
.then((response) => {
console.log(response);
const { data } = response;
if (data.status == 200) {
console.log('entered successfully');
setIsLoggedIn(true);
} else {
console.log('could not enter');
}
})
.catch((error) => {
console.log(error);
});
setPassword('');
setLogin('');
setLoginClass('');
setPasswordClass('');
}
if (isLoggedIn) {
return <Navigate to="/main" replace={true}/>
}
return
};
return (
<div className="container col-xl-10 col-xxl-8 px-4 py-5">
...
<input
type="text"
className={`form-control ${loginClass}`}
id="login"
placeholder="Login"
value={login}
onChange={(evt) => { setLogin(evt.target.value) }}
/>
...
<input
type="password"
className={`form-control ${passwordClass}`}
id="password"
placeholder="Password"
value={password}
onChange={(evt) => { setPassword(evt.target.value) }}
/>
<button className="w-100 btn btn-lg btn-primary" onClick={submit}>Enter</button>
</form>
</div>
</div>
</div>
);
}
export default Login;
Attempting to return JSX from an asynchronous callback function.
The code is attempting to issue a redirect from within the submit handler. You can't return JSX from a callback and expect it to be rendered to the DOM and have any effect. Navigate
would need to be returned from the Login
as part of the regular JSX it renders.
Attempting to access a stale closure over state.
submit
enqueues some state updates and the code incorrectly attempts to access the isLoggedIn
state to effect a redirect action.
It is generally considered a Javascript anti-pattern to mix async/await
with Promise chains. Pick one pattern or the other.
const submit = async (evt) => {
evt.preventDefault();
console.log(login, password);
if (login.length === 0 || password.length === 0) {
console.log('stop')
if (!login) {
setLoginClass('is-invalid');
}
if (!password) {
setPasswordClass('is-invalid');
}
} else {
console.log('req')
await axios
.post('http://localhost:5000/test/auth/login', {
login: login,
password: password
})
.then((response) => {
console.log(response);
const { data } = response;
if (data.status == 200) {
console.log('entered successfully');
setIsLoggedIn(true);
} else {
console.log('could not enter');
}
})
.catch((error) => {
console.log(error);
});
setPassword('');
setLogin('');
setLoginClass('');
setPasswordClass('');
}
if (isLoggedIn) { // <-- won't have value from any state enqueued updates above
return <Navigate to="/main" replace={true} />; // <-- can't do this
}
return
};
Enqueue the isLoggedIn
state update as you are and return the Navigate
component with the regular JSX to issue a declarative redirect.
function Login() {
const [login, setLogin] = useState('');
const [password, setPassword] = useState('');
const [loginClass, setLoginClass] = useState('');
const [passwordClass, setPasswordClass] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false);
const submit = async (evt) => {
evt.preventDefault();
if (!login.length || !password.length) {
if (!login) {
setLoginClass('is-invalid');
}
if (!password) {
setPasswordClass('is-invalid');
}
} else {
try {
const { data } = await axios.post(
'http://localhost:5000/test/auth/login',
{ login, password }
)
if (data.status == 200) {
setIsLoggedIn(true);
} else {
console.log('could not enter');
}
} catch(error) {
console.log(error);
};
setPassword('');
setLogin('');
setLoginClass('');
setPasswordClass('');
}
};
// Return JSX from function component
if (isLoggedIn) {
return <Navigate to="/main" replace />; // <-- declarative redirect
}
return (
...
);
}
Use the useNavigate
hook and issue an imperative redirect from the submit handler.
import { useNavigate } from 'react-router-dom';
...
function Login() {
const navigate = useNavigate();
const [login, setLogin] = useState('');
const [password, setPassword] = useState('');
const [loginClass, setLoginClass] = useState('');
const [passwordClass, setPasswordClass] = useState('');
const submit = async (evt) => {
evt.preventDefault();
if (!login.length || !password.length) {
if (!login) {
setLoginClass('is-invalid');
}
if (!password) {
setPasswordClass('is-invalid');
}
} else {
try {
const { data } = await axios.post(
'http://localhost:5000/test/auth/login',
{ login, password }
)
if (data.status == 200) {
navigate("/main", { replace: true }); // <-- imperative redirect
} else {
console.log('could not enter');
}
} catch(error) {
console.log(error);
};
setPassword('');
setLogin('');
setLoginClass('');
setPasswordClass('');
}
};
return (
...
);
}