Home > Mobile >  please explain the error to me, Error: Rendered more hooks than during the previous render
please explain the error to me, Error: Rendered more hooks than during the previous render

Time:11-05

Iam newbie and now learning to make customize react hooks here i am trying to call the function i made in app.js file, i want to use it onClick button. but fail to do so. please help me to find the error and understand it.

import React, {
  useEffect,
  useState
} from "react";

const useRandomJoke = () => {
  const [jokes, setJokes] = useState();
  useEffect(() => {
    const jokeFetch = async() => {
      await fetch("https://api.icndb.com/jokes/random")
        //we'll run 2 "then"
        .then(
          // this will give us response and will return inform of res.json
          (res) => res.json()
        ) //.json is a format
        .then((data) => {
          setJokes(data.value.joke);
        }); // now calling data from te returned values in res.json
    };
    jokeFetch();
  }, []);
  return jokes;
};

export default useRandomJoke;
//With onClick function 

function App() { const [jokes, setJokes] = useState(); 
return (
<div className="App">
  <h1>Random Jokes</h1>
  <p>{jokes}</p>
  <button onClick={()=>{setJokes(useRandomJoke)}}>
     Click for Jokes</button>
</div>
); } export default App;

`

CodePudding user response:

You can't conditionally call React hooks, like in the onClick handler of the button, as this breaks the Edit please-explain-the-error-to-me-error-rendered-more-hooks-than-during-the-previ

CodePudding user response:

useRandomJoke is a custom hook. Hooks should only be called at the top level of a component and as the custom hook already has the joke state, you don't need an additional state in the App component.

If you want to get a new joke after the component renders and every time the button gets clicked, you can do this:

const useRandomJoke = () => {
  const [joke, setJoke] = useState("");

  const fetchJoke = useCallback(() => {
    fetch("https://api.icndb.com/jokes/random")
      .then((res) => res.json())
      .then((data) => {
        setJoke(data.value.joke);
      });
  }, []);

  return [joke, fetchJoke];
};

export default function App() {
  const [joke, fetchJoke] = useRandomJoke();

  useEffect(() => {
    fetchJoke();
  }, [fetchJoke]);

  return (
    <div className="App">
      <h1>Random Jokes</h1>
      <p>{joke}</p>
      <button onClick={fetchJoke}>Click for a random joke</button>
    </div>
  );
}

Edit naughty-bird-fv05i

CodePudding user response:

Well, there is more than one point to talk about here:

1- in React.js, you can only call custom hooks at the top level of your function's body (react recognizes any function starting with the keyword use as a hook)

function App() { 
  // top level is here
  
  const randomJokes = useRandomJoke()

  const [jokes, setJokes] = useState(); 
  return (
    <div className="App">
      <h1>Random Jokes</h1>
      <p>{jokes}</p>
      <button onClick={()=>{setJokes(useRandomJoke)}}>
        Click for Jokes
       </button>
    </div>
); } 
export default App;

2- In your example I understand you want to have a new joke each time onClick triggers, in order to do so, I don't think using a custom hook is the ideal solution here, since your custom hook runs the fetchJokes method only once on initial render (as you described in your useEffect hook), I understand a lot of people mention that useEffect is the place to make API calls, but it doesn't necessarily applies to all use cases, in your example it is simple, you don't have to use useEffect neither create a custom hook.

a possible simple solution:

function App() {
  // we always call hooks at the top level of our function
  const [jokes, setJokes] = useState();
  const fetchNewJoke = () => {
  
      fetch("https://api.icndb.com/jokes/random")
        //we'll run 2 "then"
        .then(
          // this will give us response and will return inform of 
           res.json
          (res) => res.json()
        ) //.json is a format
        .then((data) => {
          setJokes(data.value.joke);
        }); // now calling data from te returned values in res.json
    };
};

    return (
     <div className="App">
      <h1>Random Jokes</h1>
      <p>{jokes}</p>
      <button onClick={fetchNewJoke}>Click for Joke</button>
     </div>
    );
} export default App;
  • Related