Home > Software engineering >  Reactjs Custom hook causing an infinite loop
Reactjs Custom hook causing an infinite loop

Time:03-07

can someone help explain why this code is causing an infinite loop and what is the best approach to fixing it please and thank you.
I am assuming its because of the useEffect within the App component which is causing a re-render which then the useState also causes a render therefore causing an infinite loop. I think I am not understanding how useEffect and useState works properly.

import "./styles.css";
import React, { useEffect, useState } from "react";

interface IObject {
  isLoading: boolean;
  isError: boolean;
  data: any[] | any;
}

function useHook1(): IObject {
  console.log("hook 1 too many re-renders?");
  return { isLoading: false, isError: false, data: [] };
}

function useHook2(): IObject {
  const result = { isLoading: false, isError: false, data: "testing" };
  console.log("hook 2 too many re-renders?");
  return result;
}

export default function App() {
  const { isLoading, isError, data } = useHook1();
  const testResult = useHook2();
  const [state, setState] = useState();

  useEffect(() => {
    console.log("inside useEffect within App")
    setState(testResult.data)
  }, [testResult])

  console.log("too many re-renders?");
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <p>{testing}</p>
    </div>
  );
}

CodePudding user response:

useHook2 returns a new object every time it runs. This means testResult is new/changed on every render, and your useEffect runs whenever testResult changes. So:

  1. Your effect updates state, which causes a re-render.

  2. On the rerender, useHook2 gets invoked and testResult is updated.

  3. testResult changed, so your effect runs again and you return to step 1, entering an infinite loop.


The solution, if I understand what you're trying to do, is to do the state management in the custom hook:

function useHook1(): IObject {
  const [state, dispatch] = useReducer(
    (state, action) => ({...state, ...action}),
    { isLoading: false, isError: false, data: [] }
  );

  return [
    state,
    dispatch
  ];
}

export default function App () {
  const [{ isLoading, isError, data }, setState] = useHook1();
  
  return isLoading ? <div>Loading</div> : (
    <div onClick={() => setState({ data: ["new data"]})}>...</div>
  )
}
  • Related