Skip to content

Commit

Permalink
fix: use useMemo to listen to change in min and max values of selecte…
Browse files Browse the repository at this point in the history
…d variable and avoid repeated state changes on component mount
  • Loading branch information
adityasingh-anyline committed Oct 21, 2024
1 parent 0d7067a commit 5456858
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 20 deletions.
54 changes: 34 additions & 20 deletions src/components/MultiRangeSlider/MultiRangeSlider.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,10 +16,12 @@ const MultiRangeSlider: FC<MultiRangeSliderProps> = ({ min, max, onChange }) =>
const maxValRef = useRef<HTMLInputElement>(null);
const range = useRef<HTMLDivElement>(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);
Expand All @@ -31,7 +34,6 @@ const MultiRangeSlider: FC<MultiRangeSliderProps> = ({ 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);
Expand All @@ -43,10 +45,30 @@ const MultiRangeSlider: FC<MultiRangeSliderProps> = ({ 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<HTMLInputElement>) => {
const value = Math.min(+event.target.value, maxVal - 1);
setMinVal(value);
event.target.value = value.toString();
},
[maxVal]
);

const handleMaxInputChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
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 (
<div className="mrs-container">
Expand All @@ -56,11 +78,7 @@ const MultiRangeSlider: FC<MultiRangeSliderProps> = ({ min, max, onChange }) =>
max={max}
value={minVal}
ref={minValRef}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
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,
})}
Expand All @@ -71,21 +89,17 @@ const MultiRangeSlider: FC<MultiRangeSliderProps> = ({ min, max, onChange }) =>
max={max}
value={maxVal}
ref={maxValRef}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
const value = Math.max(+event.target.value, minVal + 1);
setMaxVal(value);
event.target.value = value.toString();
}}
onChange={handleMaxInputChange}
className="thumb thumb--zindex-4"
/>

<div className="slider">
<div className="slider__track"></div>
<div ref={range} className="slider__range"></div>
<div className="slider__left-value" style={{ left: `${getPercent(minVal)}%` }}>
<div className="slider__left-value" style={leftValueStyle}>
{minVal}
</div>
<div className="slider__right-value" style={{ left: `${getPercent(maxVal)}%` }}>
<div className="slider__right-value" style={rightValueStyle}>
{maxVal}
</div>
</div>
Expand Down
30 changes: 30 additions & 0 deletions src/components/MultiRangeSlider/hooks/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useCallback, useEffect, useRef } from 'react';

const useDebounce = <T extends any[]>(callback: (...args: T) => void, delay: number) => {
const timeoutRef = useRef<NodeJS.Timeout>();

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;

0 comments on commit 5456858

Please sign in to comment.