简体   繁体   中英

React useEffect() : Most efficient way to compare if two arrays of objects are equal

I have a useEffect() that should trigger when an array of objects changes.

  useEffect(() => {
    setNotesToRender(props.notes)
  }, [props.notes]);

The useEffect dependency cannot compare arrays of objects, so the useEffect() in the example above triggers more often than needed. I only wish to trigger the useEffect() when the arrays differ .

An example of arrays that should trigger update

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

Things I have tested so far are:

  1. props.notes.length --> works but is not reliable - for obvious reasons:)
  2. ...props.notes --> does not work if array is empty - and may cause serious performance issues?

What is the most efficient way to compare two arrays of objects? The arrays may contain over 1000 objects.

Kind regards /K

UPDATE I need to filter out certain "notes" later using a text search, that's why I am using a React state. This is not shown in my example code.

True, useEffect cannot compare array of objects so let the useEffect hook runs.

But what you can do is to add an if-statement whether JSON.stringified notesToRender and JSON.stringified props.notes are equal.(Or you can use lodash/isEqual ) to prevent the set state method running.

However, if you think that is still not an efficient solution, we will have to see why do you need useEffect in the first place.

From your code, I think you are setting a useState hook based on the incoming props.

Thinking about that again, why would you not consider to directly render the props instead.

From official React doc page:
https://reactjs.org/docs/thinking-in-react.html#step-3-identify-the-minimal-but-complete-representation-of-ui-state

  1. Is it passed in from a parent via props? If so, it probably isn't state.
  2. Does it remain unchanged over time? If so, it probably isn't state.
  3. Can you compute it based on any other state or props in your component? If so, it isn't state.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM