Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to keep track of the object reference in a useEffect?

I’m building some API hooks and I have trouble with deps property. I want to reinvoke the useEffect when content of deps changes, instead I’m running in a forever loop. deps can be either undefined or an any[]

export const useReader = <TResponseData>({ url, deps, options }: TypeUseReader<TResponseData>) => {
    const [data, setData] = useState<TResponseData | null>(null);
    const [isLoading, setIsLoading] = useState<boolean>(false);
    const [error, setError] = useState<number | null >(null);

    async function fetchData(newUrl?: string) {}

    useEffect(() => {
       // ..fetch
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [url, deps]); // HERE, deps are forever loop

    return {
        data,
        isLoading,
        error,
        fetchData
    };
};

That’s how I’m using it:

 return useReader<FOO>(
        {
            url: 'URL_PLACEHOLDER',
            deps: [anything] /* FIXME: deps enters FOREVER LOOP */
        }
    );

What I have tried:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

[deps ?? []] and ...[deps ?? []] and deps – all of these create forever loop

>Solution :

The problem is that when you’re calling useReader with deps as an array literal, a new array is being created each time, so useEffect rightly detects a change in the value.

Broadly speaking, there are two ways to deal with this.

You can make it the responsibility of the caller – specify in the useReader interface that it accepts a single dep value of unknown type. If the caller wants to pass through an array of values, then these should be stabilized, e.g.

const dep = useMemo(() => [val1, val2], [val1, val2]);
return useReader<FOO>(
        {
            url: 'URL_PLACEHOLDER',
            isSuspended: false,
            initialValue: foo,
            dep   /* only changes if val1 or val2 has changed */
        }
    );

Easier and more flexible is to accept an array of deps (typed as unknown[]), and just spread the members of the array after the other dependencies you’re passing to useEffect:

    useEffect(() => {
        if (!isSuspended) {
            fetchData();
        }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [isSuspended, url, ...(deps ?? [])]);

The various linters do get upset about this, but it’s not problematic as far as the hook machinery is concerned.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading