Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
29
rated 0 times [  34] [ 5]  / answers: 1 / hits: 34653  / 3 Years ago, fri, february 5, 2021, 12:00:00

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 my useDispatch, 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.

More From » reactjs

 Answers
39

when 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>
);

[#50414] Tuesday, January 19, 2021, 3 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
julissaimana

Total Points: 593
Total Questions: 108
Total Answers: 112

Location: American Samoa
Member since Fri, Aug 26, 2022
2 Years ago
;