Skip to content

Commit f61c8ca

Browse files
authored
[@mantine/core] Slider: Fix onChangeEnd using stale external state (#7660)
- Introduced `onChangeEndRef` for consistent reference to `onChangeEnd`. - Replaced direct `onChangeEnd` calls with `callOnChangeEnd` utility.
1 parent c37e46d commit f61c8ca

1 file changed

Lines changed: 41 additions & 28 deletions

File tree

  • packages/@mantine/core/src/components/Slider/Slider

packages/@mantine/core/src/components/Slider/Slider/Slider.tsx

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useRef, useState } from 'react';
1+
import { useCallback, useEffect, useRef, useState } from 'react';
22
import { clamp, useMergedRef, useMove, useUncontrolled } from '@mantine/hooks';
33
import {
44
BoxProps,
@@ -211,6 +211,12 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
211211
});
212212

213213
const valueRef = useRef(_value);
214+
const onChangeEndRef = useRef(onChangeEnd);
215+
216+
useEffect(() => {
217+
onChangeEndRef.current = onChangeEnd;
218+
}, [onChangeEnd]);
219+
214220
const root = useRef<HTMLDivElement>(null);
215221
const thumb = useRef<HTMLDivElement>(null);
216222
const position = getPosition({ value: _value, min: min!, max: max! });
@@ -242,21 +248,28 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
242248
[disabled, min, max, step, precision, setValue, marks, restrictToMarks]
243249
);
244250

245-
const { ref: container, active } = useMove(
246-
handleChange,
247-
{
248-
onScrubEnd: () =>
249-
!disabled &&
250-
onChangeEnd?.(
251-
restrictToMarks && marks?.length
252-
? findClosestNumber(
253-
valueRef.current,
254-
marks.map((mark) => mark.value)
255-
)
256-
: valueRef.current
257-
),
251+
const handleScrubEnd = useCallback(() => {
252+
if (!disabled && onChangeEndRef.current) {
253+
const finalValue =
254+
restrictToMarks && marks?.length
255+
? findClosestNumber(
256+
valueRef.current,
257+
marks.map((mark) => mark.value)
258+
)
259+
: valueRef.current;
260+
onChangeEndRef.current(finalValue);
261+
}
262+
}, [disabled, marks, restrictToMarks]);
263+
264+
const { ref: container, active } = useMove(handleChange, { onScrubEnd: handleScrubEnd }, dir);
265+
266+
const callOnChangeEnd = useCallback(
267+
(value: number) => {
268+
if (!disabled && onChangeEndRef.current) {
269+
onChangeEndRef.current(value);
270+
}
258271
},
259-
dir
272+
[disabled]
260273
);
261274

262275
const handleTrackKeydownCapture = (event: React.KeyboardEvent<HTMLDivElement>) => {
@@ -269,7 +282,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
269282
if (restrictToMarks && marks) {
270283
const nextValue = getNextMarkValue(_value, marks);
271284
setValue(nextValue);
272-
onChangeEnd?.(nextValue);
285+
callOnChangeEnd(nextValue);
273286
break;
274287
}
275288

@@ -278,7 +291,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
278291
precision
279292
);
280293
setValue(nextValue);
281-
onChangeEnd?.(nextValue);
294+
callOnChangeEnd(nextValue);
282295
break;
283296
}
284297

@@ -290,7 +303,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
290303
const nextValue =
291304
dir === 'rtl' ? getPreviousMarkValue(_value, marks) : getNextMarkValue(_value, marks);
292305
setValue(nextValue);
293-
onChangeEnd?.(nextValue);
306+
callOnChangeEnd(nextValue);
294307
break;
295308
}
296309

@@ -299,7 +312,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
299312
precision
300313
);
301314
setValue(nextValue);
302-
onChangeEnd?.(nextValue);
315+
callOnChangeEnd(nextValue);
303316
break;
304317
}
305318

@@ -310,7 +323,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
310323
if (restrictToMarks && marks) {
311324
const nextValue = getPreviousMarkValue(_value, marks);
312325
setValue(nextValue);
313-
onChangeEnd?.(nextValue);
326+
callOnChangeEnd(nextValue);
314327
break;
315328
}
316329

@@ -319,7 +332,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
319332
precision
320333
);
321334
setValue(nextValue);
322-
onChangeEnd?.(nextValue);
335+
callOnChangeEnd(nextValue);
323336
break;
324337
}
325338

@@ -331,7 +344,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
331344
const nextValue =
332345
dir === 'rtl' ? getNextMarkValue(_value, marks) : getPreviousMarkValue(_value, marks);
333346
setValue(nextValue);
334-
onChangeEnd?.(nextValue);
347+
callOnChangeEnd(nextValue);
335348
break;
336349
}
337350

@@ -340,7 +353,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
340353
precision
341354
);
342355
setValue(nextValue);
343-
onChangeEnd?.(nextValue);
356+
callOnChangeEnd(nextValue);
344357
break;
345358
}
346359

@@ -350,12 +363,12 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
350363

351364
if (restrictToMarks && marks) {
352365
setValue(getFirstMarkValue(marks));
353-
onChangeEnd?.(getFirstMarkValue(marks));
366+
callOnChangeEnd(getFirstMarkValue(marks));
354367
break;
355368
}
356369

357370
setValue(min!);
358-
onChangeEnd?.(min!);
371+
callOnChangeEnd(min!);
359372
break;
360373
}
361374

@@ -365,12 +378,12 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
365378

366379
if (restrictToMarks && marks) {
367380
setValue(getLastMarkValue(marks));
368-
onChangeEnd?.(getLastMarkValue(marks));
381+
callOnChangeEnd(getLastMarkValue(marks));
369382
break;
370383
}
371384

372385
setValue(max!);
373-
onChangeEnd?.(max!);
386+
callOnChangeEnd(max!);
374387
break;
375388
}
376389

@@ -401,7 +414,7 @@ export const Slider = factory<SliderFactory>((_props, ref) => {
401414
value={scaledValue}
402415
disabled={disabled}
403416
containerProps={{
404-
ref: container as any,
417+
ref: container,
405418
onMouseEnter: showLabelOnHover ? () => setHovered(true) : undefined,
406419
onMouseLeave: showLabelOnHover ? () => setHovered(false) : undefined,
407420
}}

0 commit comments

Comments
 (0)