Home > Software design >  Issue with React hook state while working with lodash debounce
Issue with React hook state while working with lodash debounce

Time:03-01

I'm not really sure what exactly is causing the issue. So I have a component that shows the amount of pictures according to certain actions. I have a slider and a dropdown component to simulate the actions.

I am using lodash debounce on the slider. So the exact issue is that I have a state to keep track of the filter with initial value say the category with dog

const initialValue = {
  filters: [
    {
      field: "category",
      values: ["dog"]
    }
  ]
}
const [myFilter, setMyFilter] = useState(initialValue)

With the initial value above, the component will fetch all the pictures with category dog and display on the page. Then I am able to add a dropdown value to filter out say the image size, so then my state output as below:

 myFilter = {
   filters: [
     {
       field: "category",
       values: ["dog"]
     },
     {
       field: "size",
       values: ["500x500"]
     }
   ]
 }

At this point my newest state should as above. I added a debounce for the time range so that the function looks like below

onTimeRangeChange = (value) => {
  // update range state
  setRange(value);
}

const handleSetTimeRange = (value: number[]) => {
    const merge = {
      time_range: {
        start_time: moment(startTime   value[0] * 1000).toISOString(),
        end_time: moment(startTime   value[1] * 1000).toISOString(),
      },
    };

    console.log("myFilter: ", myFilter);
    setMyFilter({ ...myFilter, time_range: merge });
  };

const handleDebounce = useCallback(debounce(handleSetTimeRange, 500), [])

useEffect(() => {
  if(prevRange && oneRange !== prevRange)
  {
     handleDebounce(range)
  }
}, [range])

so myFilter after all is expected to be

  myFilter = {
    filters: [
     {
       field: "category",
       values: ["dog"]
     },
     {
       field: "size",
       values: ["500x500"]
     }
   ],
   time_range: {
        start_time: moment(startTime   value[0] * 1000).toISOString(),
        end_time: moment(startTime   value[1] * 1000).toISOString(),
   }
 }

but instead the actual value is shown as below:

myFilter = {
    filters: [
     {
       field: "category",
       values: ["dog"]
     }
   ],
   time_range: {
        start_time: moment(startTime   value[0] * 1000).toISOString(),
        end_time: moment(startTime   value[1] * 1000).toISOString(),
   }
 }

Somehow it removed the size object under the filters. Im not sure what is actually happening, Im not very familiar with debounce and useCallback. Do they reset the state?

CodePudding user response:

Looks like the useCallback hook has an empty dependency array,

const handleDebounce = useCallback(debounce(handleSetTimeRange, 500), []);

so the initial myFilter state value is closed over in callback scope and never updated.

const handleSetTimeRange = (value: number[]) => {
  const merge = {
    time_range: {
      start_time: moment(startTime   value[0] * 1000).toISOString(),
      end_time: moment(startTime   value[1] * 1000).toISOString(),
    },
  };

  console.log("myFilter: ", myFilter);
  setMyFilter({ ...myFilter, time_range: merge });
};

You can likely either add myFilter to the useCallback hook's dependency array to reenclose the updated myFilter state value in callback scope:

const handleDebounce = useCallback(
  debounce(handleSetTimeRange, 500),
  [myFilter],
);

or use a functional state update to correctly update from any previous state:

const handleSetTimeRange = (value: number[]) => {
  const merge = {
    time_range: {
      start_time: moment(startTime   value[0] * 1000).toISOString(),
      end_time: moment(startTime   value[1] * 1000).toISOString(),
    },
  };

  setMyFilter(myFilter => {
    return {
      ...myFilter,
      time_range: merge
    }
  });
};
  • Related