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
}
});
};