Search code examples
reactjsreact-hooksuse-state

useState invalid hook call in function component


I've tried for the past few days now to try and figure out why I keep getting this error-

react-dom.development.js:16227 Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

useState Component

import { useState } from 'react';

async function CreateMeta() {
  const { test, setTest } = useState();
}
export default CreateMeta;

Main Component

import CreateMeta from "./createmeta.js";

const Minter = (props) => {
  const onPressed = async () => {
    await CreateMeta();  
  };
  
  return (
    <>
      <Button onClick={onPressed}>Test</Button>
    </>
  )
}

export default Minter;

Which is then called in other components -

return (
  <>
    <Minter />
  </>
)

I've tried several suggestions but nothing works and my code seems to be correct. Im able to useState in some of my other components only if it's set in the same component.

Whenever I try calling a useState component instead of rendering in the return, I get the invalid hook error. Same happens with other "use" Hooks. What am I doing wrong?


Solution

  • Main issue:

    React hooks can be used only inside body of rendering function (Functional Component). In your example "useState" is called inside click handler, so it will not work.

    To fix this error you should move 'useState' directly into some rendering function body or into some hook.

    My guess solution:

    I'm not 100% sure what you need in your exact case, but may guess:

    1. Rename function CreateMeta to be useCreateMeta (so it will be a hook) and don't make it async.
    2. Inside this hook, apart state (pay attention - use square brackets with useState), define a callback with all expected async logic inside and return this callback from this hook:
    import { useState, useCallback } from 'react';
    
    function useCreateMeta() {
     const [test, setTest] = useState(); // <-- square brackets here (array, not object)
     const createMeta = useCallback(async () => {
        // do whatever you need inside, e.g.:
        // set state:
        setTest('creating meta...');
        // Do some async things:
        await new Promise(resolve => setTimeout(() => {
          setTest('meta created');
          resolve();
        }, 1000));
     }, []);
     return {createMeta};
    }
    
    export default useCreateMeta;
    
    
    1. Call this hook right inside Minter components like this:
    function Minter () {
      // ...
      const {createMeta} = useCreateMeta();
      const onPressed = async () => {
        await createMeta();  
      };
      // ...
    }
    

    Example of the code see here: https://codesandbox.io/s/cool-cookies-9be20n