Invalid Hook Call. Hooks Can Only Be Called Inside of the Body of a Function Component

Invalid hook call. Hooks can only be called inside of the body of a function component

You can only call hooks from React functions. Read more here.

Just convert the Allowance class component to a functional component.

Working CodeSandbox demo.

const Allowance = () => {
const [allowances, setAllowances] = useState([]);

useEffect(() => {
fetch("http://127.0.0.1:8000/allowances")
.then(data => {
return data.json();
})
.then(data => {
setAllowances(data);
})
.catch(err => {
console.log(123123);
});
}, []);

const classes = useStyles();
return ( <
Paper className = {
classes.root
} >
<
Table className = {
classes.table
} >
<
TableHead >
<
TableRow >
<
TableCell > Allow ID < /TableCell> <
TableCell align = "right" > Description < /TableCell> <
TableCell align = "right" > Allow Amount < /TableCell> <
TableCell align = "right" > AllowType < /TableCell> <
/TableRow> <
/TableHead> <
TableBody > {
allowances.map(row => ( <
TableRow key = {
row.id
} >
<
TableCell component = "th"
scope = "row" > {
row.AllowID
} <
/TableCell> <
TableCell align = "right" > {
row.AllowDesc
} < /TableCell> <
TableCell align = "right" > {
row.AllowAmt
} < /TableCell> <
TableCell align = "right" > {
row.AllowType
} < /TableCell> <
/TableRow>
))
} <
/TableBody> <
/Table> <
/Paper>
);
};

export default Allowance;

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body

Sir. I think you made a typo and the error is due to that.
Change useNavigate to useNavigate()

import React, { useEffect, useState } from 'react'
import { Button, Container, Form } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
import ErrorMessageBox from '../components/ErrorMessagebox'
import Loading from '../components/Loading'

export const SignInScreen = () => {
const navigate = useNavigate()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')

const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'

const userLogin = useSelector((state) => state.userLogin)
const { userInfo, loading, error } = userLogin
const dispatch = useDispatch()

useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])

const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}

return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
{error && <ErrorMessageBox variant="danger">{error}</ErrorMessageBox>}
{loading && <Loading />}
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}

React call from class to functional component. Error: Invalid hook call. Hooks can only be called inside of the body of a function component

React hooks can only be called in function components or custom React hooks. In order to use the navigate function you'll need to either create a wrapper component or a custom withNavigate HOC to pass navigate as a prop to your class component.

Wrapper

const MyComponentWithNavigation = props => {
const navigate = useNavigate();

return <MyClassComponent {...props} navigate={navigate} />;
};

Usage:

<Route path="..." element={<MyComponentWithNavigation />} />

HOC

const withNavigate = Component => props => {
const navigate = useNavigate();

return <Component {...props} navigate={navigate} />;
};

Usage:

const MyComponentWithNavigation = withNavigation(MyClassComponent);

...

<Route path="..." element={<MyComponentWithNavigation />} />

Accessing the navigate function

In your class component, access navigate from props.

this.props.navigate("/");

React 17.0.1: Invalid hook call. Hooks can only be called inside of the body of a function component

You're calling this loginForm as if it was a plain function, not as a React component:

const loginForm = LoginForm({
onToken: token => {
const url = new URL(window.location.href);
const returnurl = url.searchParams.get('returnurl');
if (returnurl) {
window.location.href = decodeURIComponent(returnurl);
} else {
window.location.href = '/';
}
}
});

ReactDOM.render(loginForm, domContainer);

As a result, it doesn't get wrapped in React.createElement, so it thinks the hook call is invalid.

Use JSX syntax instead:

const onToken = token => {
const url = new URL(window.location.href);
const returnurl = url.searchParams.get('returnurl');
if (returnurl) {
window.location.href = decodeURIComponent(returnurl);
} else {
window.location.href = '/';
}
}

ReactDOM.render(<LoginForm onToken={onToken} />, domContainer);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

React - Error: Invalid hook call. Hooks can only be called inside of the body of a function component. What is wrong my syntax?

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


Related Topics



Leave a reply



Submit