Home > OS >  Why useEffect makes infinitive loop if pass the second param as array and inside put empty object
Why useEffect makes infinitive loop if pass the second param as array and inside put empty object

Time:04-10

I have object with filter params for API and this object I pass to useEffect.

    let dataForFilter = typeof defaultFormData[someKey] !== 'undefined'
    ? defaultFormData[someKey]
    : {};

    for (let key in props.route.params) {
        if (props.route.params.hasOwnProperty(key)) {
            dataForFilter[key] = props.route.params[key];
        }
    }

    useEffect(() => {
        async function fetchData() {
            const body = {
               ...dataForFilter,
            };
            loadData(url, body);
        }
        fetchData();
    }, [dataForFilter]);

By default the object is empty but this makes infinite loop if I pass empty object in array as second parameter. I don't understand why? Please could somebody explain me why and how this solve.

CodePudding user response:

Every time the function runs, it creates a new empty object here:

let dataForFilter = typeof defaultFormData[someKey] !== 'undefined'
    ? defaultFormData[someKey]
    : {};

So the dependency array's values change every render, if defaultFormData[someKey] doesn't exist. The empty object isn't equal to itself.

console.log([{}] === [{}]);
console.log({} === {});

Create the object outside the function instead, so that its reference is stable.

const emptyObj = {};
const MyComponent = ({ defaultFormData }) => {
  // ...
  let dataForFilter = typeof defaultFormData[someKey] !== 'undefined'
    ? defaultFormData[someKey]
    : emptyObj;

CodePudding user response:

In your useEffect, call a function to rerender the component. In your useEffect dependencies (the array at the end), you passed in dataForFilter as dependency. That means, whenever dataForFilter changes, the useEffect will be called again. But since the useEffect makes the component rerender and dataForFilter is not defined as state but as normal variable inside the component, it will be defined again with another value (Notice: Even an object with the same values is not the same object as before). If you want any useEffect to be called once when the component renders, pass an empty array as dependencies like this:

let dataForFilter = typeof defaultFormData[someKey] !== 'undefined' // <-- dataForFilter gets evaluated again and changes its value, which calls the useEffect
    ? defaultFormData[someKey]
    : {};

    for (let key in props.route.params) {
        if (props.route.params.hasOwnProperty(key)) {
            dataForFilter[key] = props.route.params[key];
        }
    }

    useEffect(() => {
        async function fetchData() {
            const body = {
               ...dataForFilter,
            };
            loadData(url, body); // <-- You rerender the component somewhere in this function, dataForFilter gets defined again and is another object (with the same value BUT still another object)
        }
        fetchData();
    }, []); // <-- Empty array as dependency, only gets called once
  • Related