Skip to content

Commit 204f286

Browse files
authored
fix: solve the issue that input-number's value auto change after fast click step handle in safari (#585)
* fix: solve the issue that input-number's value auto change after fast click step handle in safari * feat: add test case
1 parent 8750366 commit 204f286

File tree

2 files changed

+53
-7
lines changed

2 files changed

+53
-7
lines changed

src/StepHandler.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import * as React from 'react';
33
import classNames from 'classnames';
44
import useMobile from 'rc-util/lib/hooks/useMobile';
5+
import raf from 'rc-util/lib/raf';
56

67
/**
78
* When click and hold on a button - the speed of auto changing the value.
@@ -32,13 +33,20 @@ export default function StepHandler({
3233
}: StepHandlerProps) {
3334
// ======================== Step ========================
3435
const stepTimeoutRef = React.useRef<any>();
36+
const frameIds = React.useRef<number[]>([]);
3537

3638
const onStepRef = React.useRef<StepHandlerProps['onStep']>();
3739
onStepRef.current = onStep;
3840

41+
const onStopStep = () => {
42+
clearTimeout(stepTimeoutRef.current);
43+
};
44+
45+
3946
// We will interval update step when hold mouse down
4047
const onStepMouseDown = (e: React.MouseEvent, up: boolean) => {
4148
e.preventDefault();
49+
onStopStep();
4250

4351
onStepRef.current(up);
4452

@@ -53,11 +61,10 @@ export default function StepHandler({
5361
stepTimeoutRef.current = setTimeout(loopStep, STEP_DELAY);
5462
};
5563

56-
const onStopStep = () => {
57-
clearTimeout(stepTimeoutRef.current);
58-
};
59-
60-
React.useEffect(() => onStopStep, []);
64+
React.useEffect(() => () => {
65+
onStopStep();
66+
frameIds.current.forEach(id => raf.cancel(id));
67+
}, []);
6168

6269
// ======================= Render =======================
6370
const isMobile = useMobile();
@@ -74,11 +81,18 @@ export default function StepHandler({
7481
[`${handlerClassName}-down-disabled`]: downDisabled,
7582
});
7683

84+
// fix: https://github.com/ant-design/ant-design/issues/43088
85+
// In Safari, When we fire onmousedown and onmouseup events in quick succession,
86+
// there may be a problem that the onmouseup events are executed first,
87+
// resulting in a disordered program execution.
88+
// So, we need to use requestAnimationFrame to ensure that the onmouseup event is executed after the onmousedown event.
89+
const safeOnStopStep = () => frameIds.current.push(raf(onStopStep));
90+
7791
const sharedHandlerProps = {
7892
unselectable: 'on' as const,
7993
role: 'button',
80-
onMouseUp: onStopStep,
81-
onMouseLeave: onStopStep,
94+
onMouseUp: safeOnStopStep,
95+
onMouseLeave: safeOnStopStep,
8296
};
8397

8498
return (

tests/longPress.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,36 @@ describe('InputNumber.LongPress', () => {
3535
});
3636
await waitFor(() => expect(onChange).toHaveBeenCalledWith(14));
3737
});
38+
39+
it('Simulates event calls out of order in Safari', async () => {
40+
const onChange = jest.fn();
41+
const { container } = render(<InputNumber defaultValue={20} onChange={onChange} />);
42+
fireEvent.mouseDown(container.querySelector('.rc-input-number-handler-up'));
43+
act(() => {
44+
jest.advanceTimersByTime(10);
45+
});
46+
fireEvent.mouseUp(container.querySelector('.rc-input-number-handler-up'));
47+
act(() => {
48+
jest.advanceTimersByTime(10);
49+
});
50+
fireEvent.mouseUp(container.querySelector('.rc-input-number-handler-up'));
51+
act(() => {
52+
jest.advanceTimersByTime(10);
53+
});
54+
fireEvent.mouseDown(container.querySelector('.rc-input-number-handler-up'));
55+
act(() => {
56+
jest.advanceTimersByTime(10);
57+
});
58+
fireEvent.mouseDown(container.querySelector('.rc-input-number-handler-up'));
59+
act(() => {
60+
jest.advanceTimersByTime(10);
61+
});
62+
fireEvent.mouseUp(container.querySelector('.rc-input-number-handler-up'));
63+
64+
act(() => {
65+
jest.advanceTimersByTime(600 + 200 * 5 + 100);
66+
});
67+
68+
await waitFor(() => expect(onChange).toBeCalledTimes(3));
69+
});
3870
});

0 commit comments

Comments
 (0)