DEV Community

conormdowney
conormdowney

Posted on

Invalid hook call.

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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)