4

My React app uses a slider component from Material UI. Its onChange event calls a function to update the state. I realized that my component rerenders for every slider step which is why I want to delay the updating of the state for like 300ms after the last time the slider was moved.

My approach is to start a timer with the state update via onChange. When the onChange is called again, the previous timer should be canceled though. This is the part I still struggle with.

The setSliderValue is a function which takes a number to update the state. How do I clear "timer" only when sliderChangeHandler is called again?

const [sliderValue, setSliderValue] = useState(20);

const sliderChangeHandler = (event, newValue) => {
    const timer = setTimeout(setSliderValue, 300, newValue);
    clearTimeout(timer);
};
Rtholl
  • 75
  • 4

2 Answers2

2

You should set setTimeout return value in your state:

const [sliderValue, setSliderValue] = useState(20);
const [timer, setTimer] = useState();

const sliderChangeHandler = (event, newValue) => {
    clearTimeout(timer);
    const newTimer = setTimeout(setSliderValue, 300, newValue);
    setTimer(newTimer);
};

I would suggest to you to use this lib for debouncing any function: https://github.com/xnimorz/use-debounce

In your case it would be:

const [sliderValue, setSliderValue] = useState(20);

const debounced = useDebouncedCallback(
  // function
  setSliderValue,
  // delay in ms
  300,
);

// some part of the code
debounced.callback(30);

In this case every time you call debounced.callback it will cancel previous call

Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
0

I use a useRef hook usually for debounce. Something like:

const timeoutRef = useRef(null)

const [sliderValue, setSliderValue] = useState(20)

const onChange = (e, v) => {
  clearTimeout(timeoutRef.current)
  timeoutRef.current = setTimeout(setSliderValue, 300, v)
}

Saves a line of code ;)

nihilok
  • 1,325
  • 8
  • 12