Hi,
Im pretty new to react and ive run into the Invalid hook call issue. Googling around i see the most common fix is for when there are two instances of react. When I run npm ls react I only see 1.
The issue only arose for me after I added a Context to my app but I have no idea what the issue is.
Im using a custom hook to do server calls. If they fail I want to show the errors in a modal. I have the Modal in my App component but it is not open until there are errors. Below is my code.
My App.tsx
import {
defaultTheme,
Grid,
Provider as ProviderV3,
} from "@adobe/react-spectrum";
import { FC, useState, useEffect, useContext } from "react";
import Toolbar from './components/Toolbar';
import OwnerSearch from './components/OwnerSearch/OwnerSearch';
import NewOwnerSearch from './components/OwnerSearch/NewOwnerSearch';
import GlobalContext, { GlobalContextProvider } from './api/GlobalContext';
import './App.scss';
import Modal from "./components/Modal";
// // Add this in node_modules/react-dom/index.js
// window.React1 = require('react');
// // Add this in your component file
// require('react-dom');
// window.React2 = require('react');
// console.log('Conor: ' + window.React1 === window.React2);
const App: FC = () => {
const ctx = useContext(GlobalContext)
const [isModalOpen, setIsModalOpen] = useState(false);
const [errors, setErrors] = useState(['']);
let modal = (
<Modal
heading="Warning"
content={<div>{ctx.errors.join(';')}</div>}
buttonList={
[
{ label: "OK", clickHandler: () => { }, closesModal: true },
{ label: "Cancel", clickHandler: () => { alert("cancelled") }, closesModal: false }
]
}
isOpen={isModalOpen}
setIsOpen={setIsModalOpen} />
);
useEffect(() => {
if (ctx.errors.length > 0) {
setIsModalOpen(true);
}
}, [ctx.errors]);
//
return (
<GlobalContext.Provider value={{errors: errors, setErrors: setErrors}}>
<ProviderV3 theme={defaultTheme}>
<Toolbar></Toolbar>
<Grid
margin='25px'
columns='50% 50%'
gap='10px'
maxWidth='100vw'>
<OwnerSearch />
<NewOwnerSearch />
</Grid>
</ProviderV3>
{modal}
</GlobalContext.Provider>
);
};
export default App;
my component that uses my hook to get data:
import {
TextField,
ProgressCircle,
Form,
Flex,
Button
} from '@adobe/react-spectrum';
import '@spectrum-web-components/card/sp-card.js';
import { FC, useState, useEffect, useContext } from 'react';
import GlobalContext from '../../api/GlobalContext';
import useHttp from '../../hooks/useHttp';
import React from 'react';
import './OwnerSearch.scss';
declare global {
namespace JSX {
interface IntrinsicElements {
'sp-card': React.DetailedHTMLProps<ModHTMLAttributes<HTMLElement>, HTMLElement>;
}
}
}
const NewOwnerSearch: FC = () => {
const ctx = useContext(GlobalContext);
const [owners, setOwners] = useState([]);
const [searchText, setSearchText] = useState('msft');
const { isLoading, error, sendRequest: setOwnersData } = useHttp({url: 'http://liberate-api.com/api/ownership/' + searchText}, setOwners);
const handleSubmit = (e: any /*JS Submit event*/) => {
e.preventDefault();
setOwnersData();
}
useEffect(() =>
{
setOwnersData();
}, [setOwnersData]);
if(error != '')
{
//ctx.addError(error);
ctx.setErrors((prevErrors: string[]) =>
{
//prevErrors.push(err.message)
let newArray = prevErrors.map((er) => {return er});
newArray.push(error);
return newArray;
}
);
}
const numberWithCommas = (x: number) => {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
return (
<sp-card
heading='Owner Data'
subheading='Search Public Owner Data'>
<div slot='footer'>
<Flex
direction='column'>
<Form onSubmit={(e) => handleSubmit(e)}>
<Flex
direction='row'>
<TextField
marginEnd='15px'
placeholder='Search Owners'
onChange={(a) => setSearchText(a)}
value={searchText}
/>
<Button type='submit' variant='cta'>
Search Data
</Button>
</Flex>
</Form>
{
(isLoading) ?
(<Flex justifyContent='center'>
<ProgressCircle aria-label="Loading…" isIndeterminate />
</Flex>) :
(<Flex
direction='column'>
{owners.map((x: any, y: number) => {
return (
<div
key={x.ownerId}
className={(y % 2 == 0) ? 'even' : 'odd'}>
<Flex
justifyContent='space-between'>
<p>{x.ownerName}</p>
<p>{numberWithCommas(x.sharesHeld)}</p>
</Flex>
</div>
)
})}
</Flex>)
}
</Flex>
</div>
</sp-card>
);
};
export default React.memo(NewOwnerSearch);
and my custom hook:
import {useCallback, useState} from 'react';
const useHttp = (requestObj: any, setData: Function) =>
{
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const sendRequest = useCallback(() =>
{
setIsLoading(true);
setError('');
fetch(requestObj.url, {
method: requestObj.method ? requestObj.method: 'GET',
headers: requestObj.headers ? requestObj.headers : {},
body: requestObj.body ? JSON.stringify(requestObj.body) : null
})
.then(res => res.json())
.then(data => {
setIsLoading(false);
setData(data);
})
.catch(err =>
{
setError(err.message);
setIsLoading(false);
console.log('There was an error');
});
}, []);
return {
isLoading: isLoading,
error: error,
sendRequest: sendRequest
}
}
export default useHttp;
finally, here is the error im getting:
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.
at resolveDispatcher (react.development.js:1476)
at useContext (react.development.js:1484)
at useProvider (module.js:239)
at $bc3300334f45fd1ec62a173e70ad86$var$Provider (module.js:95)
at describeNativeComponentFrame (react-dom.development.js:946)
at describeFunctionComponentFrame (react-dom.development.js:1034)
at describeFiber (react-dom.development.js:1119)
at getStackByFiberInDevAndProd (react-dom.development.js:1138)
at createCapturedValue (react-dom.development.js:20023)
at throwException (react-dom.development.js:20351)
Top comments (0)