kentcdodds / use-deep-compare-effect

🐋 It's react's useEffect hook, except using deep comparison on the inputs, not reference equality
https://npm.im/use-deep-compare-effect
MIT License
1.88k stars 84 forks source link

useDeepCompareEffect() not working as expected #64

Open Stophface opened 1 year ago

Stophface commented 1 year ago

Relevant code or config


import withObservables from '@nozbe/with-observables';
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import {compose} from 'recompose';
import {Q} from '@nozbe/watermelondb';
import useDeepCompareEffect from 'use-deep-compare-effect';

const Foo = ({arrayWithDeeplyNestedObjects}) => {
  console.log('render'); // <-- this renders whenever arrayWithDeeplyNestedObjects is updated

  useDeepCompareEffect(() => {
    console.log(new Date()); // <-- this does not render whenever arrayWithDeeplyNestedObjects is updated
    const doSomething = async () => {
        ...
    };
    doSomething();
  }, [arrayWithDeeplyNestedObjects]);

    return <SomeNiceUi />
}

export default compose(
  withDatabase,
  withObservables(['arrayWithDeeplyNestedObjects'], ({database}) => ({
    arrayWithDeeplyNestedObjects: database.get(SOME_TABLE).query().observe(),
  })),
)(Foo);

What you did: I implement the useDeepCompareEffect() instead of the native useEffect()hook.

Problem description: The useDeepCompareEffect() is not executing, when the object is updated. This one

export default compose(
  withDatabase,
  withObservables(['arrayWithDeeplyNestedObjects'], ({database}) => ({
    arrayWithDeeplyNestedObjects: database.get(SOME_TABLE).query().observe(),
  })),
)(Foo);

is part of an offline first database (watermelonDB) and updates arrayWithDeeplyNestedObjects when there is a change in the database. I would expect useDeepCompareEffect() to execute, whenever arrayWithDeeplyNestedObjects changes, but it is not. This is how arrayWithDeeplyNestedObjects looks like

[{"__changes": null, "_isEditing": false, "_preparedState": null, "_raw": {"_changed": "x,y", "_status": "created", "id": "3", "x": 5851, "id_arr": "[\"160\"]", "id": "6wmwep5xwfzj3dav", "y": 0.17576194444444446}, "_subscribers": [], "collection": {"_cache": [RecordCache], "_subscribers": [Array], "changes": [Subject], "database": [Database], "modelClass": [Function SomeTable]}}]

The changes to arrayWithDeeplyNestedObjects are done in the objects either to x, y or id_arr. The length of arrayWithDeeplyNestedObjects can change as well. There might be more (or less) objects of the same structure in there.

Why isn't useDeepCompareEffect() executed, when arrayWithDeeplyNestedObjects changes?

dmytro-shchurov commented 1 year ago

In my case the hook stops compare properly after the first successful comparison. const ref = React.useRef<T>(value) already has a value of dependencies (value: T) at a moment these two values are compared in if (!deepEqual(value, ref.current)) at a line 36.

I created a ref externally to that hook, and passed the ref through method calls. In this case the ref keeps a previous value. Otherwise it seems that ref and `value' share a same object reference.

For me it looks like a very strange behavior of useRef() that might be caused with some decorator from some library.

UPD It does not matter whether the dependencies are memoized or not. The original useDeepCompare from react-use has the same problem on my set of libraries.

MAfzalKhan1997 commented 1 year ago

In my case the hook stops compare properly after the first successful comparison. @dmytro-shchurov same for me

Naught0 commented 1 year ago

I am having a similar issue while depending on the value from the watch() method from react-hook-form. I am able to reproduce the behavior here:

https://codesandbox.io/s/modest-scooby-zpn2c7?file=/src/App.js

Modifying the input value does not seem to trigger the effect more than once. Any idea as to why?

E: I found this in the hook form docs:

watch result is optimised for render phase instead of useEffect's deps, to detect value update you may want to use an external custom hook for value comparison.

I'm guessing hook-form is the issue rather than this lib

E2: I was able to resolve this by using a slightly modified version of this library which does a cloneDeep from this comment here