diff --git a/src/components/MultiRangeSlider/MultiRangeSlider.tsx b/src/components/MultiRangeSlider/MultiRangeSlider.tsx index 4cac1a5..feb2db4 100644 --- a/src/components/MultiRangeSlider/MultiRangeSlider.tsx +++ b/src/components/MultiRangeSlider/MultiRangeSlider.tsx @@ -1,6 +1,7 @@ -import React, { ChangeEvent, FC, useCallback, useEffect, useState, useRef } from 'react'; +import React, { ChangeEvent, FC, useCallback, useEffect, useState, useRef, useMemo } from 'react'; import classnames from 'classnames'; import './multiRangeSlider.css'; +import useDebounce from './hooks/useDebounce'; interface MultiRangeSliderProps { min: number; @@ -15,10 +16,12 @@ const MultiRangeSlider: FC = ({ min, max, onChange }) => const maxValRef = useRef(null); const range = useRef(null); - // Convert to percentage - const getPercent = useCallback((value: number) => Math.round(((value - min) / (max - min)) * 100), [min, max]); + const debouncedOnChange = useDebounce((min: number, max: number) => onChange({ min, max }), 250); + + const getPercent = useMemo(() => { + return (value: number) => Math.round(((value - min) / (max - min)) * 100); + }, [min, max]); - // Set width of the range to decrease from the left side useEffect(() => { if (maxValRef.current) { const minPercent = getPercent(minVal); @@ -31,7 +34,6 @@ const MultiRangeSlider: FC = ({ min, max, onChange }) => } }, [minVal, getPercent]); - // Set width of the range to decrease from the right side useEffect(() => { if (minValRef.current) { const minPercent = getPercent(+minValRef.current.value); @@ -43,10 +45,30 @@ const MultiRangeSlider: FC = ({ min, max, onChange }) => } }, [maxVal, getPercent]); - // Get min and max values when their state changes useEffect(() => { - onChange({ min: minVal, max: maxVal }); - }, [minVal, maxVal, onChange]); + debouncedOnChange(minVal, maxVal); + }, [minVal, maxVal, debouncedOnChange]); + + const handleMinInputChange = useCallback( + (event: ChangeEvent) => { + const value = Math.min(+event.target.value, maxVal - 1); + setMinVal(value); + event.target.value = value.toString(); + }, + [maxVal] + ); + + const handleMaxInputChange = useCallback( + (event: ChangeEvent) => { + const value = Math.max(+event.target.value, minVal + 1); + setMaxVal(value); + event.target.value = value.toString(); + }, + [minVal] + ); + + const leftValueStyle = useMemo(() => ({ left: `${getPercent(minVal)}%` }), [getPercent, minVal]); + const rightValueStyle = useMemo(() => ({ left: `${getPercent(maxVal)}%` }), [getPercent, maxVal]); return (
@@ -56,11 +78,7 @@ const MultiRangeSlider: FC = ({ min, max, onChange }) => max={max} value={minVal} ref={minValRef} - onChange={(event: ChangeEvent) => { - const value = Math.min(+event.target.value, maxVal - 1); - setMinVal(value); - event.target.value = value.toString(); - }} + onChange={handleMinInputChange} className={classnames('thumb thumb--zindex-3', { 'thumb--zindex-5': minVal > max - 100, })} @@ -71,21 +89,17 @@ const MultiRangeSlider: FC = ({ min, max, onChange }) => max={max} value={maxVal} ref={maxValRef} - onChange={(event: ChangeEvent) => { - const value = Math.max(+event.target.value, minVal + 1); - setMaxVal(value); - event.target.value = value.toString(); - }} + onChange={handleMaxInputChange} className="thumb thumb--zindex-4" />
-
+
{minVal}
-
+
{maxVal}
diff --git a/src/components/MultiRangeSlider/hooks/useDebounce.ts b/src/components/MultiRangeSlider/hooks/useDebounce.ts new file mode 100644 index 0000000..41f8b0e --- /dev/null +++ b/src/components/MultiRangeSlider/hooks/useDebounce.ts @@ -0,0 +1,30 @@ +import { useCallback, useEffect, useRef } from 'react'; + +const useDebounce = (callback: (...args: T) => void, delay: number) => { + const timeoutRef = useRef(); + + const debouncedCallback = useCallback( + (...args: T) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + timeoutRef.current = setTimeout(() => { + callback(...args); + }, delay); + }, + [callback, delay] + ); + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return debouncedCallback; +}; + +export default useDebounce;