nolan
nolan

Reputation: 467

Why this react hook cause infinite loop?

This piece of code will run infinitely, but no matter removing the useValue(object) or useTest() will stop the infinite run. This whole thing does not make sense to me, can somebody explain it?

https://codesandbox.io/s/zen-currying-v2zu1?file=/src/App.js

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

export default function App() {
  // this code can also stop the infinite loop

  // const [object] = useState({
  //   name: "",
  //   personId: ""
  // });
  const object = {
    name: "",
    personId: ""
  };
  // strange thing: remove useValue or useTest can stop the ifinite loop
  useValue(object);
  useTest();
  // this console will run infinitely
  console.log(object);
  return <div className="App">{"123"}</div>;
}

export const useTest = () => {
  const [a, setA] = useState(1);
  useEffect(() => {
    setA(2);
  }, []);
};

const useValue = (value) => {
  const [a, setA] = useState(value);
  useEffect(() => {
    setA(value);
  }, [value]);
  // return a
};

Upvotes: 0

Views: 585

Answers (3)

FelHa
FelHa

Reputation: 1103

Because object is a reference type, its identity will change every time you rerender App. There you use a custom hook useValue and pass object into it. Since you also register it as value in the dependency array of useEffect, useEffect will be triggered, setting a new state, ending in rerendering and looping.

  1. Make sure, you save the identity of reference types (useMemo or useCallback).
  2. Read the docs about useEffect. Its behaviour strongly depends on how you use its dependency array.

Upvotes: 1

Anh Nhat Tran
Anh Nhat Tran

Reputation: 562

Because you passed an object in the dependencies. useEffect with do a shallow comparison so it always renders.

There are some solutions. Read here. You could modify your useValue hook to use JSON.stringify as below.

const useValue = (value) => {
    const [a, setA] = useState(value);
    useEffect(() => {
        setA(value);
    }, [JSON.stringify(value)]);
    // return a
};

Upvotes: 1

Antonio Erdeljac
Antonio Erdeljac

Reputation: 3244

Make sure to memoize your object in order to properly use it in dependencies.

const { useMemo } from 'react';

const object = useMemo(
  () => ({
    name: "",
    personId: ""
  }),
  []
);

The reason behind the infinite loop is because you're providing a non-memoized object into your dependency array in useEffect.

Object comparison in Javascript is more complex than just comparing obj1 === obj2, as there is a reference that needs to be taken into account.

Read more about comparing objects in javascript here

Upvotes: 1

Related Questions