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 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.
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.
Share
Improve this question
edited Feb 5, 2021 at 3:29
yung peso
asked Feb 5, 2021 at 1:55
yung pesoyung peso
1,7668 gold badges37 silver badges84 bronze badges
2 Answers
Reset to default 12when you call Logout
at onClick
you are calling hooks from your function Logout
outside of a component body, what's is not allowed.
You can abstract your Logout logic to a custom hook where it returns the logoutUser
function:
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 }
};
then you consume your custom hook at your LoginUser
component body, extracting the logoutUser
method:
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 safely logout user since no hooks are being called
onClick={function(){ handleLogout(); logoutUser()}}
>
Do you want to logout?
</Button>
</Container>
};
return (
<Container>
{user ? <UserLoggedInAlert/> : <SignIn/> }
</Container>
);
}
export default LoginUser;
you can take a step ahead and simplify your LogOut
component:
export default function LogOut() {
const { logoutUser } = useLogOut();
useEffect(() => {
logoutUser()
});
return <div>Logout</div>;
};
Edit
for simplicity at your Logout.js do as:
export const 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 }
};
export default function LogOut() {
const { logoutUser } = useLogOut();
useEffect(() => {
logoutUser()
});
return <div>Logout</div>;
};
at your your Login you import the hook with curly brackets:
import { useLogOut } from '../auth/Logout';
and at your index.js without the curly brackets you import the component:
import LogOut from './components/auth/Logout';
const routing = (
<Router>
<Route path="/logout" component={LogOut} />
</Router>
);
Components are basically function references, and you are invoking them, so instead of doing SignIn()
, try <SignIn />
edit: and make userLoggedInAlert
start with upper case, then all together:
{user ? <UserLoggedInAlert /> : <SignIn />}