Use apiOptions as state value

I'm not sure how you are consuming the custom hook but making apiOptions a state value by using useState should work just fine. This way you can serve it to your custom hook as a state value like so:

const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)

This way it's going to change only when you use setApiOptions.

Example #1

import { useState, useEffect } from 'react';

const useExample = (apiOptions) => {
  const [data, updateData] = useState([]);
  
  useEffect(() => {
    console.log('effect triggered')
  }, [apiOptions]);

  return {
    data
  };
}
export default function App() {
  const [apiOptions, setApiOptions] = useState({ a: 1 })
  const { data } = useExample(apiOptions);
  const [somethingElse, setSomethingElse] = useState('default state')

  return <div>
    <button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
    <button onClick={() => { setSomethingElse('state') }}>
      change something else to force rerender
    </button>
  </div>;
}

Alternatively

You could write a deep comparable useEffect as described here:

function deepCompareEquals(a, b){
  // TODO: implement deep comparison here
  // something like lodash
  // return _.isEqual(a, b);
}

function useDeepCompareMemoize(value) {
  const ref = useRef() 
  // it can be done by using useMemo as well
  // but useRef is rather cleaner and easier

  if (!deepCompareEquals(value, ref.current)) {
    ref.current = value
  }

  return ref.current
}

function useDeepCompareEffect(callback, dependencies) {
  useEffect(
    callback,
    dependencies.map(useDeepCompareMemoize)
  )
}

You can use it like you'd use useEffect.

Answer from lenilsondc on Stack Overflow
Top answer
1 of 7
115

Use apiOptions as state value

I'm not sure how you are consuming the custom hook but making apiOptions a state value by using useState should work just fine. This way you can serve it to your custom hook as a state value like so:

const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)

This way it's going to change only when you use setApiOptions.

Example #1

import { useState, useEffect } from 'react';

const useExample = (apiOptions) => {
  const [data, updateData] = useState([]);
  
  useEffect(() => {
    console.log('effect triggered')
  }, [apiOptions]);

  return {
    data
  };
}
export default function App() {
  const [apiOptions, setApiOptions] = useState({ a: 1 })
  const { data } = useExample(apiOptions);
  const [somethingElse, setSomethingElse] = useState('default state')

  return <div>
    <button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
    <button onClick={() => { setSomethingElse('state') }}>
      change something else to force rerender
    </button>
  </div>;
}

Alternatively

You could write a deep comparable useEffect as described here:

function deepCompareEquals(a, b){
  // TODO: implement deep comparison here
  // something like lodash
  // return _.isEqual(a, b);
}

function useDeepCompareMemoize(value) {
  const ref = useRef() 
  // it can be done by using useMemo as well
  // but useRef is rather cleaner and easier

  if (!deepCompareEquals(value, ref.current)) {
    ref.current = value
  }

  return ref.current
}

function useDeepCompareEffect(callback, dependencies) {
  useEffect(
    callback,
    dependencies.map(useDeepCompareMemoize)
  )
}

You can use it like you'd use useEffect.

2 of 7
30

If the input is shallow enough that you think deep equality would still be fast, consider using JSON.stringify:

const useExample = (apiOptions) => {
    const [data, updateData] = useState([]);
    const apiOptionsJsonString = JSON.stringify(apiOptions);

    useEffect(() => {
       const apiOptionsObject = JSON.parse(apiOptionsJsonString);
       doSomethingCool(apiOptionsObject).then(response => {               
           updateData(response.data);
       })
    }, [apiOptionsJsonString]);

    return {
        data
    };
};

Note it won’t compare functions.

🌐
GitHub
github.com › kentcdodds › use-deep-compare-effect
GitHub - kentcdodds/use-deep-compare-effect: 🐋 It's react's useEffect hook, except using deep comparison on the inputs, not reference equality
There's often a better way to do what you're trying to do than a deep comparison. React's built-in useEffect hook has a second argument called the "dependencies array" and it allows you to optimize when React will call your effect callback.
Starred by 1.9K users
Forked by 86 users
Languages   TypeScript 98.6% | JavaScript 1.4%
Discussions

reactjs - How can I deep compare objects/arrays in useEffect/React? - Stack Overflow
As you see, I was using lodash's isEqual to compare whether it's the same object/array or not. How to do it in hooks? How does React compare when I pass nested objects/arrays to the dependency array of useEffect? Does it do a deep comparison or shallow? More on stackoverflow.com
🌐 stackoverflow.com
Does useEffect compare deeply?
No. It's a shallow comparison of values directly inside the deps array, ie, prevDeps[i] !== currentDeps[i]. No deep comparisons involved. More on reddit.com
🌐 r/reactjs
9
4
September 8, 2022
React useEffect() : Most efficient way to compare if two arrays of objects are equal
If the second option is not desired you could use lodash.deepCompare or create your own function, takea look at How to make data input readonly, but showing calendar? and simply check object by object while iterating. ... meta property like version and whenever we update the object,, that's a good way of doing it, it's kind of how databases handle optimistic locking. And if the data is large it's certainly the most efficient way.. 2020-12-04T09:27:56.813Z+00:00 ... True, useEffect cannot compare array ... More on stackoverflow.com
🌐 stackoverflow.com
What if you need to check an object in the useEffect dependency array?
Passing stateful objects to the useEffect is fine? If it’s coming from redux the only time the reference would change is dispatching an action that would change them. The only problem with passing non primitives to useEffects is when you create them during the render, making the reference unstable More on reddit.com
🌐 r/reactjs
11
2
August 2, 2023
🌐
DEV Community
dev.to › hey_yogini › useeffect-dependency-array-and-object-comparison-45el
UseEffect dependency array and object comparison! - DEV Community
January 5, 2022 - In my opinion the real take-away for an article with a topic like yours should be: a) just go for it and put all props you depend on in the dependency array, or b) stringify the object you're depending on (might be slow and unreliable) or c) utilize an external library like github.com/kentcdodds/use-deep-com... or write a custom useEffect hook yourself - it's basically just comparing the previous version of an object (stored via useRef()) with the current one, it's no magic
🌐
DEV Community
dev.to › vsramalwan › ways-to-handle-deep-object-comparison-in-useeffect-hook-1elm
Ways to Handle Deep Object Comparison in useEffect hook - DEV Community
May 26, 2020 - Use useDeepCompareEffect hook as a drop-in replacement for useEffect hook for objects · import useDeepCompareEffect from 'use-deep-compare-effect' ...
🌐
Medium
medium.com › suyeonme › react-lets-deep-dive-into-deps-array-of-useeffect-13ab96468db7
React: Let’s deep dive into deps array of useEffect | by Suyeon Kang | suyeonme | Medium
July 31, 2022 - If we want to run useEffect based on the object(reference type), we should compare dependencies with deep equal.
🌐
egghead.io
egghead.io › lessons › react-handle-deep-object-comparison-in-react-s-useeffect-hook-with-the-useref-hook
Handle Deep Object Comparison in React's useEffect hook with the useRef Hook | egghead.io
The second argument to React's useEffect hook is an array of dependencies for your useEffect callback. When any value in that array changes, the effect ...
Published   November 29, 2018
🌐
React
react.wiki › home › react hooks › useeffect dependency array: deep mechanics explained
useEffect Dependency Array: Deep Mechanics Explained | react.wiki
January 1, 2026 - After each render, React compares the new dependency array with the previous one, element by element, using strict equality. ... function UserProfile({ userId }: { userId: number }) { const [user, setUser] = useState(null); useEffect(() => { ...
Find elsewhere
🌐
YouTube
youtube.com › anuj singla
How React compare useEffect dependency array object (previous object and new object) | useEffect - YouTube
How React compare useEffect dependency array object (previous object and new object)Github repo for question and answers: https://github.com/anujsingla/inter...
Published   March 4, 2022
Views   844
🌐
Habr
habr.com › en › articles › 752780
React Custom Hook: useDeepCompareEffect / Habr
August 21, 2023 - Created to tackle the limitations of the default useEffect hook, useDeepCompareEffect ensures that the effect callback is only triggered when the dependencies have deeply changed, using lodash's isEqual function for accurate comparison.
🌐
Marcoghiani
marcoghiani.com › blog › how-to-use-the-react-hook-usedeepeffect
UseDeepEffect Hook in React: A Guide to Better Performance
February 17, 2020 - We do the same with the prevDeps ... Using the Array.propotype.every method, iterate over the current dependencies array and compare each one of them with the previous value....
🌐
Usehooks
usehooks.io › home › documentation › lifecycle › usedeepcompareeffect
useDeepCompareEffect - React Hook | useHooks.io
January 9, 2026 - A React hook that works like useEffect but performs deep comparison on dependencies instead of shallow comparison. Useful when dependencies are objects or arrays that might be recreated on each render.
🌐
Reddit
reddit.com › r/reactjs › does useeffect compare deeply?
Does useEffect compare deeply? : r/reactjs
September 8, 2022 - It's a shallow comparison of values directly inside the deps array, ie, prevDeps[i] !== currentDeps[i]. No deep comparisons involved. ... React generally expects that data updates are done immutably, which always results in new object references. If you're doing that, [oldObj] will be different ...
🌐
Profy.dev
profy.dev › article › react-useeffect-with-object-dependency
React useEffect and objects as dependency - 4 approaches to avoid unnecessary re-renders
March 19, 2024 - Another approach is using this useDeepCompareEffect created by Kent C. Dodds. This hook is similar to the native useEffect, but instead of comparing the dependencies by reference, it makes a deep comparison of all values inside an object.
🌐
Medium
medium.com › @rmandloi › react-js-comparing-useeffect-and-usedeepcompareeffect-09ac72724b7c
React.js: Comparing useEffect and useDeepCompareEffect | by Rmandloi | Medium
August 26, 2024 - If you pass objects or arrays as ... useDeepCompareEffect: This is a custom hook (usually from a utility library like react-use) that works similarly to useEffect, but it performs a deep comparison of the dependency values....
🌐
JavaScript in Plain English
javascript.plainenglish.io › a-less-known-trick-with-react-hook-dependencies-8dafaca7a150
A Less-known Trick with React Hook Dependencies | JavaScript in Plain English
August 8, 2022 - As React developers, we were taught to ensure the referential equality of non-primitive data types like objects and arrays when we are using them in any hooks with a dependency array as an argument. Any reference change to these data types will trigger a re-computation since React only does shallow comparison. Join Medium for free to get updates from this writer. ... A common practice is to memoize these data types. Another way is forcing deep comparison checks with the combination of ref , memoization and a deep equal function like what use-deep-compare-effect does.
Top answer
1 of 3
4

What is the most efficient way

The most efficient way is likely a custom compare for this particular shape of data. You could use JSON.stringify or some other generic comparing function, but it's not likely to be the most efficient for obvious reasons.

So in your example a simple compare function might look like ->

const array1 = [{id:1,title:'one'},{id:2,title:'two'},{id:3,title:'three'}];
const array2 = [{id:1,title:'one'},{id:2,title:'two'},{id:3,title:'four'}];

const arrayIsEqual = (a1, a2) => 
  a1 === a2 ||
  a1.length === a2.length &&
  a1.every((f,i) => 
    f.id === a2[i].id &&
    f.title === a2[i].title
  )
  
  
//tests
const a1clone = [...array1];

//different arrays not equal
console.log(arrayIsEqual(array1, array2)); //false

//different arrays equal
console.log(arrayIsEqual(array1, a1clone)); //true

//different arrays same values, but different order
a1clone.reverse();
console.log(arrayIsEqual(array1, a1clone)); //false

//same arrays, fastest check
console.log(arrayIsEqual(array1, array1)); //true

2 of 3
4

First of all 1k objects is not a large number of objects, but getting back to the question: the easiest way is to use

JSON.stringify(object1) === JSON.stringify(object2);

But it is not the most efficient, when performance is an issue, the smartest thing I could think of is to use a meta property like version and whenever we update the object in any way we increment the version as well. Then all you have to do is to check two arrays based on id + version mapping.

If the second option is not desired you could use lodash.deepCompare or create your own function, takea look at How to make data input readonly, but showing calendar? and simply check object by object while iterating.

🌐
DevPress
devpress.csdn.net › react › 62ec516219c509286f416bd6.html
Ways to Handle Deep Object Comparison in useEffect hook_reactnative_weixin_0010034-React
August 5, 2022 - Use useDeepCompareEffect hook as a drop-in replacement for useEffect hook for objects · import useDeepCompareEffect from 'use-deep-compare-effect' ...
🌐
Bram.us
bram.us › 2020 › 03 › 05 › react-usedeepcompareeffect-hook-a-useeffect-using-deep-comparison
React useDeepCompareEffect Hook: A useEffect using deep comparison – Bram.us
March 5, 2020 - March 5, 2020March 17, 2020 1 Comment ... a second argument called the “dependencies array” and it allows you to optimize when React will call your effect callback....