Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useEffect not triggering when object property in dependence array

Tags:

reactjs

I have a context/provider that has a websocket as a state variable. Once the socket is initialized, the onMessage callback is set. The callback is something as follows:

const wsOnMessage = (message: any) => {
    const data = JSON.parse(message.data);
    setProgress(merge(progress, data.progress));
};

Then in the component I have something like this:

function PVCListTableRow(props: any) {
    const { pvc } = props;
    const { progress } = useMyContext();

    useEffect(() => {
        console.log('Progress', progress[pvc.metadata.uid])
    }, [progress[pvc.metadata.uid]])

    return (
        {/* stuff */}
    );

}

However, the effect isn't triggering when the progress variable gets updated.

The data structure of the progress variable is something like

{
    "uid-here": 0.25,
    "another-uid-here": 0.72,
    ...etc,
}

How can I get the useEffect to trigger when the property that matches pvc.metadata.uid gets updated?

Or, how can I get the component to re-render when that value gets updated?

like image 303
cclloyd Avatar asked Nov 18 '25 18:11

cclloyd


1 Answers

Quoting the docs:

The function passed to useEffect will run after the render is committed to the screen.

And that's the key part (that many seem to miss): one uses dependency list supplied to useEffect to limit its invokations, but not to set up some conditions extra to that 'after the render is committed'.

In other words, if your component is not considered updated by React, useEffect hooks just won't be called!

Now, it's not clear from your question how exactly your context (progress) looks like, but this line:

setProgress(merge(progress, data.progress));

... is highly suspicious.

See, for React to track the change in object the reference of this object should change. Now, there's a big chance setProgress just assignes value (passed as its parameter) to a variable, and doesn't do any cloning, shallow or deep.

Yet if merge in your code is similar to lodash.merge (and, again, there's a huge chance it actually is lodash.merge; JS ecosystem is not that big these days), it doesn't return a new object; instead it reassigns values from data.progress to progress and returns the latter.

It's pretty easy to check: replace the aforementioned line with...

setProgress({ ...merge(progress, data.progress) });

Now, in this case a new object will be created and its value will be passed to setProgress. I strongly suggest moving this cloning inside setProgress though; sure, you can do some checks there whether or not you should actually force value update, but even without those checks it should be performant enough.

like image 116
raina77ow Avatar answered Nov 21 '25 08:11

raina77ow



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!