You can pass JSON.stringify(outcomes) as the dependency list:

Read more here

useEffect(() => {
  console.log(outcomes)
}, [JSON.stringify(outcomes)])
Answer from Loi Nguyen Huynh on Stack Overflow
Top answer
1 of 7
195

You can pass JSON.stringify(outcomes) as the dependency list:

Read more here

useEffect(() => {
  console.log(outcomes)
}, [JSON.stringify(outcomes)])
2 of 7
23

Using JSON.stringify() or any deep comparison methods may be inefficient, if you know ahead the shape of the object, you can write your own effect hook that triggers the callback based on the result of your custom equality function.

useEffect works by checking if each value in the dependency array is the same instance with the one in the previous render and executes the callback if one of them is not. So we just need to keep the instance of the data we're interested in using useRef and only assign a new one if the custom equality check return false to trigger the effect.

function arrayEqual(a1: any[], a2: any[]) {
  if (a1.length !== a2.length) return false;
  for (let i = 0; i < a1.length; i++) {
    if (a1[i] !== a2[i]) {
      return false;
    }
  }
  return true;
}

type MaybeCleanUpFn = void | (() => void);

function useNumberArrayEffect(cb: () => MaybeCleanUpFn, deps: number[]) {
  const ref = useRef<number[]>(deps);

  if (!arrayEqual(deps, ref.current)) {
    ref.current = deps;
  }

  useEffect(cb, [ref.current]);
}

Usage

function Child({ arr }: { arr: number[] }) {
  useNumberArrayEffect(() => {
    console.log("run effect", JSON.stringify(arr));
  }, arr);

  return <pre>{JSON.stringify(arr)}</pre>;
}

Taking one step further, we can also reuse the hook by creating an effect hook that accepts a custom equality function.

type MaybeCleanUpFn = void | (() => void);
type EqualityFn = (a: DependencyList, b: DependencyList) => boolean;

function useCustomEffect(
  cb: () => MaybeCleanUpFn,
  deps: DependencyList,
  equal?: EqualityFn
) {
  const ref = useRef<DependencyList>(deps);

  if (!equal || !equal(deps, ref.current)) {
    ref.current = deps;
  }

  useEffect(cb, [ref.current]);
}

Usage

useCustomEffect(
  () => {
    console.log("run custom effect", JSON.stringify(arr));
  },
  [arr],
  (a, b) => arrayEqual(a[0], b[0])
);

Live Demo

๐ŸŒ
React
react.dev โ€บ reference โ€บ react โ€บ useEffect
useEffect โ€“ React
See the difference between passing an array of dependencies, an empty array, and no dependencies at all. ... useEffect is a Hook, so you can only call it at the top level of your component or your own Hooks.
Discussions

Passing array to useEffect dependency list

Your gut is correct, as far as I can tell. How are you updating the array? Some context would be helpful if possible.

If you're using push/pop/mutating a value in the array, it will be referentially equal (oldArray === mutatedArray) and the effect will not be rerun.

More on reddit.com
๐ŸŒ r/reactjs
20
4
August 21, 2019
reactjs - React useEffect dependency array and implementation - Stack Overflow
I'm new to react hooks and have run into a situation which I solved, but not sure if this is a proper implementation of useEffect. I have a form with some fields (answer, question etc) with some More on stackoverflow.com
๐ŸŒ stackoverflow.com
reactjs - Passing a function in the useEffect dependency array causes infinite loop - Stack Overflow
I faced issues and bugs when I used to add functions into the dependency array of useEffect. I created a simple custom hook that solves the problem of javascript closures (function in useEffect needs point to fresh objects), I separated the use of reactive elements and elements that need to ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
reactjs - useEffect and dependency array - Stack Overflow
I have read that placing an object as a dependency in the useEffect hook does not work because of how objects are compared between rerenders. More on stackoverflow.com
๐ŸŒ stackoverflow.com
People also ask

Why is the dependency array important in useEffect?
The dependency array in useEffect tells React when to re-run the effect. It ensures the effect only runs when specified state or prop values change, improving performance and avoiding unnecessary renders.
๐ŸŒ
dhiwise.com
dhiwise.com โ€บ post โ€บ understanding-the-importance-of-the-useeffect-dependency-array-in-react
UseEffect Dependency Array
What is the useEffect hook used for in React?
The useEffect hook in React allows developers to manage side effects like data fetching, DOM manipulation, or subscriptions within functional components. It helps control when these side effects should be executed by specifying dependencies.
๐ŸŒ
dhiwise.com
dhiwise.com โ€บ post โ€บ understanding-the-importance-of-the-useeffect-dependency-array-in-react
UseEffect Dependency Array
What happens if you donโ€™t pass a dependency array to useEffect?
Without a dependency array, the effect runs after every render. This can lead to performance issues and bugs since the effect won't re-run based on changes in specific dependencies.
๐ŸŒ
dhiwise.com
dhiwise.com โ€บ post โ€บ understanding-the-importance-of-the-useeffect-dependency-array-in-react
UseEffect Dependency Array
๐ŸŒ
DhiWise
dhiwise.com โ€บ post โ€บ understanding-the-importance-of-the-useeffect-dependency-array-in-react
UseEffect Dependency Array
August 13, 2025 - The dependency array is the second parameter you pass to the useEffect hook. It tells React when to re-run your useEffect callback function.
๐ŸŒ
Retool
retool.com โ€บ blog โ€บ hooks-and-state-102-the-dependency-array-in-useeffect
Hooks and state 102: the Dependency array in useEffect()
July 9, 2025 - But the linter complains that `isDesktop` is not in the dependency array. The correct solution here is to use `isDesktop` and `selectedPatient` to initialize component state variables.
๐ŸŒ
Zipy
zipy.ai โ€บ blog โ€บ react-useeffect-dependency-array
React useEffect Dependency Array Guide
May 23, 2024 - The component's state can be updated or side effects can be carried out using this function. Passing an empty array as the dependent parameter for the useEffect hook in React is one of the easiest ways to use it.
Find elsewhere
๐ŸŒ
egghead.io
egghead.io โ€บ lessons โ€บ react-manage-the-useeffect-dependency-array
Manage the useEffect dependency array | egghead.io
[0:54] React useEffect accepts a second argument as an optimization to combat this problem. That second argument is a dependency array where you pass all the dependencies for your side effect.
Published ย  March 10, 2020
๐ŸŒ
Reddit
reddit.com โ€บ r/reactjs โ€บ passing array to useeffect dependency list
r/reactjs on Reddit: Passing array to useEffect dependency list
August 21, 2019 -

I'm having an issue with useEffect not firing if an array in the dependency list changes.

//props.shapes = ['stuff', 'in', 'the', 'array']
useEffect(() => {
   // do stuff
  }, [props.shapes])

Nothing in that useEffect runs. This...should work right? If the array changes higher up in the component tree a new array is generated and that should trigger a re-render in this component and then the useEffect will fire, right?

What's weird is if I make it dependent on the length, it does work

//props.shapes = ['stuff', 'in', 'the', 'array']
useEffect(() => {
   // do stuff
  }, [props.shapes.length])

My gut tells me that this array is actually getting mutated somewhere and that is causing the issue...but I don't see that in the app. What am I fundamentally misunderstanding about arrays (and I guess objects) with useEffect?

Ninja Edit: I know that using .length in the array dependency will lead to bugs if the array values change but the length doesn't. I don't plan on using this, just explaining what I'm experiencing.

๐ŸŒ
Medium
medium.com โ€บ devil-is-in-the-details โ€บ dependency-array-in-useeffect-hook-d73e0ef2ab33
Dependency array in useEffect hook | by Shreejit Rajbanshi | Devil is in the Details | Medium
November 20, 2022 - This might be the most common form used with useEffect. We pass an array filled with all the dependencies that should trigger the callback function to run. Change in any of the variable in the list will execute the callback.
Top answer
1 of 3
112

The issue is that upon each render cycle, markup is redefined. React uses shallow object comparison to determine if a value updated or not. Each render cycle markup has a different reference. You can use useCallback to memoize the function though so the reference is stable. Do you have the react hook rules enabled for your linter? If you did then it would likely flag it, tell you why, and make this suggestion to resolve the reference issue.

const markup = useCallback(
  (count) => {
    const stringCountCorrection = count + 1;
    return (
      // Some markup that references the sections prop
    );
  },
  [/* any dependencies the react linter suggests */]
);

// No infinite looping, markup reference is stable/memoized
useEffect(() => {
  if (sections.length) {
    const sectionsWithMarkup = sections.map((section, index) => markup(index));
    setSectionBlocks(blocks => [...blocks, ...sectionsWithMarkup]);
  } else {
    setSectionBlocks([]);
  }
}, [sections, markup]);

Alternatively if the markup function is only used in the useEffect hook you can move it directly into the hook callback to remove it as an external dependency for the hook.

Example:

useEffect(() => {
  const markup = (count) => {
    const stringCountCorrection = count + 1;
    return (
      // Some markup that references the sections prop
    );
  };

  if (sections.length) {
    const sectionsWithMarkup = sections.map((section, index) => markup(index));
    setSectionBlocks(blocks => [...blocks, ...sectionsWithMarkup]);
  } else {
    setSectionBlocks([]);
  }
}, [sections, /* any other dependencies the react linter suggests */]);

Additionally, if the markup function has absolutely no external dependencies, i.e. it is a pure function, then it could/should be declared outside any React component.

2 of 3
9

Why is an infinite loop created when I pass a function expression

The "infinite loop" is the component re-rendering over and over because the markup function is a NEW function reference (pointer in memory) each time the component renders and useEffect triggers the re-render because it's a dependency.

The solution is as @drew-reese pointed out, use the useCallback hook to define your markup function.

๐ŸŒ
Devtrium
devtrium.com โ€บ posts โ€บ dependency-arrays
What are dependency arrays in React? - Devtrium
August 8, 2021 - So the rule is pretty simple, but as I said there's some exceptions. If a value is defined outside of a component, then it is fixed and won't change when the app is running. Thus React doesn't need you to add it to the dependency array.
Top answer
1 of 3
5

useEffect will re-run with an object in it's dependency array whenever that object changes references.

For example in your code, user is always stable until you call setUser in which case it will change to a new reference and run at that point.

If you instead defined user as a new object in each render, then the useEffect would instead run every time the component re-rendered.

  const user = {
    firstName: 'Joe',
    lastName: 'Smith',
    address: {
      home: '123 street',
    },
  };

It's all about referential equality. Whether previousUser===newUser on each render. React essentially performs a check very similar to that (I believe Object.is) for every variable in the dependency array.

2 of 3
1

Bellow your component.

changes:

  • move initialState outside UseEffectHook. You don't need to create initialState every re-render;
  • use useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed;
  • setUser and setCount with updater function;
  • handleOnChangeUser able to use for all fields of user object. Every time handleOnChangeUser - create new ref to user object. in this case useEffect able to detect changes;

import React, { useCallback, useEffect, useState } from 'react';

const initialState = {
  firstName: "Joe",
  lastName: "Smith",
  address: {
    home: "123 street",
  }
};

export function UseEffectHook() {
  const [user, setUser] = useState(initialState);
  const [count, setCount] = useState(0);
  const handleOnChangeUser = useCallback((event) => {
    const { name, value } = event;
    
    setUser(prevState => ({ ...prevState, [name]: value }));
  }, []);
  
  const increaseCount = useCallback(() => {
    setCount(prevState => prevState + 1);
  }, []);

  useEffect(() => {
    console.log("triggered");
  }, [user]);

  return (
    <div>
      <p>useEffect - Practice with different deps</p>
      {JSON.stringify(user)}

      <label>
        <span>Firstname:</span>

        <input
          type="text"
          name="firstName"
          onChange={handleOnChangeUser}
        />
      </label>
      
      <br />
      
      <label>
        <span>Lastname:</span>
        
        <input
          type="text"
          name="lastName"
          onChange={handleOnChangeUser}
        />
      </label>

      <button onClick={increaseCount}>Increase</button>
   </div>
  );
}

๐ŸŒ
Medium
egebilge.medium.com โ€บ best-practices-for-useeffect-dependency-arrays-in-react-fcb53ef55495
๐ŸŽฏ Best Practices for useEffect Dependency Arrays in React | by Ege Bilge | Medium
April 24, 2025 - โ€œThe function passed to useEffect will run after the render is committed to the screen.โ€ โ€” React Docs ยท React encourages developers to focus on actual data dependencies, not function identities โ€” especially when dealing with state setters or memoized callbacks. By excluding setter functions from your dependency arrays: ... Whether youโ€™re writing production code or creating reusable hooks, applying these best practices helps you avoid subtle bugs and makes your code easier to maintain.
Top answer
1 of 2
7

You can make a custom hook to do what you want:

In this example, we replace the last element in the array, and see the output in the console.

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { isEqual } from "lodash";

const usePrevious = value => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

const App = () => {
  const [arr, setArr] = useState([2, 4, 5]);
  const prevArr = usePrevious(arr);

  useEffect(() => {
    if (!isEqual(arr, prevArr)) {
      console.log(`array changed from ${prevArr} to ${arr}`);
    } 
  }, [prevArr]);

  const change = () => {
    const temp = [...arr];
    temp.pop();
    temp.push(6);
    setArr(temp);
  };

  return (
      <button onClick={change}>change last array element</button>
  )
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Live example here.

2 of 2
1

Your effect is triggered based on the "shopUsers" prop, which itself triggers a redux action that updates the "shopUsers" prop and thats why it keeps infinitely firing.

I think what you want to optimize is the rendering of your component itself, since you're already using redux, I'm assuming your props/state are immutable, so you can use React.memo to re-render your component only when one of its props change.

Also you should define your state/props variable outside of your hooks since they're used in the scope of the entire function like so.

In your case, if you pass an empty array as a second param to memo, then it will only fire on ComponentDidMount, if you pass null/undefined or dont pass anything, it will be fired on ComponentDidMount + ComponentDidUpdate, if you want to optimise it that even when props change/component updates the hook doesn't fire unless a specific variable changes then you can add some variable as your second argument

React.memo(function(props){
  const [isLoading, setLoading] = useState(false);
  const { getStoreUsers, shopUsers } = props;
  useEffect(() => {
    setLoading(true);
    getStoreUsers().then(() => {
      setLoading(false);
    }).catch((err) => {
      setLoading(false);
    });
  }, []);
...
})
๐ŸŒ
GitHub
github.com โ€บ facebook โ€บ react โ€บ issues โ€บ 18243
Bug: Passing an array as a `useEffect` dependency causes an infinite loop ยท Issue #18243 ยท facebook/react
March 7, 2020 - import React, { useEffect, useState } from 'react'; export default function Home() { const args = ['a']; const [value, setValue] = useState(['b']); useEffect(() => { setValue(['c']); }, [args]); console.log('value', value); } Notice in the code above, that I don't even use or modify args at all, in the useEffect callback. If I change the value of args to a string, like const args = 'a', then there is no endless loop. So the problem seems to occur when the dependency is an array.
Author ย  garyking
๐ŸŒ
Medium
medium.com โ€บ @joabi โ€บ react-patterns-optimization-of-dependency-array-in-useeffect-b551d172fc76
React Patterns: Optimization of Dependency Array in useEffect | by Julie J. | Medium
November 4, 2023 - When we work on improving the ... for the useEffect hook. This list let React to avoid re-running the effect every time the component is rendered. However, there's another useful practices that can take this optimization a step further. Everying goes well if we have primitive values in the dependency array. The problem occurs when we want to pass a dependency ...
๐ŸŒ
Codedamn
codedamn.com โ€บ news โ€บ react js
useEffect dependency array in React.js โ€“ Complete Guide
June 2, 2023 - The useEffect manages an array that contains the state variables or functions which are kept an eye for any changes. These changes then trigger the callback function. The most basic dependency array would be an empty array.