Home > Back-end >  useState invalid hook call in functional component
useState invalid hook call in functional component

Time:08-20

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?

CodePudding user response:

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

CodePudding user response:

You should define useState hook like below

import { useState } from 'react';

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

and It will work.

CodePudding user response:

Is CreateMeta supposed to be a React component? Components cannot be async and cannot be called (like you do in onPressed). It's a bit unclear to me what you are trying to do, but you should probably have your state in the Minter component and the onPressed callback can change the state.

CodePudding user response:

It is what it says. You cannot invoke hooks outside of a component.

import { useState } from 'react';

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

You code does not show a component. Just a function...

import { useState } from 'react';

export const CreateMeta = (props) => {
 const { test, setTest } = useState();
}

EDIT1:

I am doing it this way:

import React from "react";
import { useState } from "react";
import { getUser } from "./api";

export const MainMenu = (props) => {
  const [user, setUser] = useState({});

  function handleClick(event) {
    //this would replace async
    getUser(10).then((response) => {
      setUser(response.data);
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Klick me</button>
      {user && <div>{user.firstname}</div>}
    </div>
  );
};

API

import { axios } from "../services/api"

export const getUser = (userId) => {
    return axios.get("/url",{userId : userId }); //this will returns a promise
}

Or you can await and async

import React from "react";
import { useState } from "react";
import { getUser } from "./api";

export const MainMenu = (props) => {
  const [user, setUser] = useState({});

  const handleClick = async (event) => {
    const response = await getUser(10);
    setUser(response.data);
  }

  return (
    <div>
      <button onClick={handleClick}>Klick me</button>
      {user && <div>{user.firstname}</div>}
    </div>
  );
};

API

import { axios } from "../services/api"

export const getUser = async (userId) => {
    const response = await axios.get("/url",{userId : userId }); //this will returns a promise
    return response;
}
  • Related