To give a little background:
I'm trying to build a Login Form Page function that detects if the user is already logged. If they are logged in, then a button will appear prompting the user to log out. Like so: {user ? userLoggedInAlert() : SignIn()}
If user
is True, then the button appears, if user
is false, then they receive the username and password form for login authentication.
I have nailed down all the functionality.
However, when I press on button to logout the user out, I receive this error:
×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Here is my code, I think there is something wrong with the const userLoggedInAlert
import React from 'react';
import { useSelector } from 'react-redux';
import { selectUser } from '../../features/userSlice';
import SignIn from '../auth/Login';
import Container from '@material-ui/core/Container';
import { Button } from '@material-ui/core';
import { logout } from "../../features/userSlice";
import { NavLink } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import LogOut from '../auth/Logout';
function LoginUser()
{
const user = useSelector(selectUser);
const dispatch = useDispatch();
const handleLogout = (e) =>
{
if(e) e.preventDefault();
dispatch(logout());
};
const userLoggedInAlert = () =>
{
return <Container>
<span>
You are already logged in!
</span>
<Button
component={NavLink}
variant="contained"
color="primary"
to="/logout"
onClick={function(){ LogOut(); handleLogout()}}
>
Do you want to logout?
</Button>
</Container>
};
return (
<Container>
{user ? userLoggedInAlert() : SignIn()}
</Container>
);
}
export default LoginUser;
SignIn()
works just fine, so does myuseDispatch
, and other components necessary to create the code above. I'm pretty sure I just messed up the syntax somewhere in the code above?
Here is my code to LogOut()
, note that I can logout just fine through other means using this exact same component.
import React, { useEffect } from 'react';
import axiosInstance from '../../axios';
import { useHistory } from 'react-router-dom';
export default function LogOut() {
const history = useHistory();
useEffect(() => {
const response = axiosInstance.post('user/logout/blacklist/', {
refresh_token: localStorage.getItem('refresh_token'),
});
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
axiosInstance.defaults.headers['Authorization'] = null;
history.push('/login');
});
return <div>Logout</div>;
};
Thank you for any help, I'm stuck here.
EDIT:
"dependencies": {
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@reduxjs/toolkit": "^1.5.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.6.0",
"axios": "^0.21.1",
"chart.js": "^2.9.4",
"react": "^17.0.1",
"react-chartjs-2": "^2.11.1",
"react-dom": "^17.0.1",
"react-facebook-login": "^4.1.1",
"react-hook-form": "^6.14.2",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"web-vitals": "^0.2.4"
},
EDIT EDIT:
I'm struggling to implement the recommended changes, I'm now getting some more errors that are making me confused:
Uncaught Error: Objects are not valid as a React child (found: object with keys {logoutUser}). If you meant to render a collection of children, use an array instead.
The above error occurred in the <useLogOut>
component.
Here is how I'm implementing:
LoginPortal.js
import React from 'react';
import { useSelector } from 'react-redux';
import { selectUser } from '../../features/userSlice';
import SignIn from '../auth/Login';
import Container from '@material-ui/core/Container';
import { Button } from '@material-ui/core';
import { logout } from "../../features/userSlice";
import { NavLink } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import useLogOut from '../auth/Logout';
function LoginUser()
{
const user = useSelector(selectUser);
// use hook at component body extracting logoutUser method
const { logoutUser } = useLogOut();
const dispatch = useDispatch();
const handleLogout = (e) =>
{
if(e) e.preventDefault();
dispatch(logout());
};
const UserLoggedInAlert = () =>
{
return <Container>
<span>
You are already logged in!
</span>
<Button
component={NavLink}
variant="contained"
color="primary"
to="/logout"
// here now you can save logout user since no hooks are being called
onClick={function(){ logoutUser(); handleLogout()}}
>
Do you want to logout?
</Button>
</Container>
};
return (
<Container>
{user ? <UserLoggedInAlert/> : <SignIn/> }
</Container>
);
}
export default LoginUser;
Logout.js
component:
import axiosInstance from '../../axios';
import { useHistory } from 'react-router-dom';
export default function useLogOut() {
const history = useHistory();
// we don't useEffect here, we are only interested in function logoutUser
const logoutUser = () => {
const response = axiosInstance.post('user/logout/blacklist/', {
refresh_token: localStorage.getItem('refresh_token'),
});
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
axiosInstance.defaults.headers['Authorization'] = null;
history.push('/login');
}
return { logoutUser }
};
My index.js:
import useLogOut from './components/auth/Logout';
const routing = (
<Router>
<Route path="/logout" component={useLogOut} />
</Router>
);
ReactDOM.render(routing, document.getElementById('root'));
Would this be the correct path for my index router now that Ive changed it?
New errors:
Error: Objects are not valid as a React child (found: object with keys {logoutUser}). If you meant to render a collection of children, use an array instead.