Skip to content

fix: number is duplicate when input number by keyboard's numeric keypad in Chrome #709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/InputNumber.tsx
Original file line number Diff line number Diff line change
@@ -160,6 +160,7 @@ const InternalInputNumber = React.forwardRef(
const userTypingRef = React.useRef(false);
const compositionRef = React.useRef(false);
const shiftKeyRef = React.useRef(false);
const imeCompositionRef = React.useRef(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

单独靠 compositionRef 解决不了这个?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compositionRef 感觉就是用来做类似处理的,是不是改 compositionRef 的逻辑就可以了?避免新增这个 imeCompositionRef 吧?

Copy link
Author

@Joelei Joelei Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yoyo837 compositionRef 不够的。

chrome环境下,日文输入法,小键盘连续键入数字 1234, 不按Enter键,点击页面其他地方。输入框内的数字变成了 $ 11,234。

这个问题的主要原因就是在onCompositionEnd 之后 还有一次input的 onChange 事件被触发,这个时候compositionRef.current的值已经为false了。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个事件顺序是符合预期的:

onCompositionStart -> onCompositionEnd -> onBlur

退出后触发一次收集值也是符合预期的行为,能录个屏看看不?我想了解一下这个输入法的行为是什么样子的。

Copy link
Author

@Joelei Joelei Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zombieJ 可以的。输入的时候用的是小键盘,NumLock开启。

2025-04-10.11-49-21.mp4

Copy link
Author

@Joelei Joelei Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zombieJ Hello, 我这里是win11,我再传一个视频看一下,你选择的是微软日语输入法吗?

2025-04-14.11.38.51.mp4

输入的时候使用的是键盘右边部分的数字键盘:

image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我刚才试了一个win8,行为也不一样:输入1234的时候显示的是全角的1234,点外侧后正确填写1,234没有问题。切换输入法为半角后就是直接输入数字,也没有问题。
我晚上回去再试一下win10。

Copy link
Author

@Joelei Joelei Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zombieJ 我试了win10,表现是和win11一样的,bug是存在的。使用的是键盘右边部分的数字键盘。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我 windows 电脑坏了,等我修好再试试这个右侧键盘…… T T
 
另外你普通的数字键意思是不会有这个现象的?

Copy link
Author

@Joelei Joelei Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zombieJ 是的,上排普通的数字键没有问题,两边的事件类型好像不一样。


// ============================ Value =============================
// Real value control
@@ -428,6 +429,9 @@ const InternalInputNumber = React.forwardRef(

// >>> Input
const onInternalInput: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (imeCompositionRef.current && !compositionRef.current) {
return;
}
collectInputValue(e.target.value);
};

@@ -498,6 +502,12 @@ const InternalInputNumber = React.forwardRef(

shiftKeyRef.current = shiftKey;

if (event.key === 'Process' || compositionRef.current) {
imeCompositionRef.current = true;
} else {
imeCompositionRef.current = false;
}

if (key === 'Enter') {
if (!compositionRef.current) {
userTypingRef.current = false;
@@ -543,6 +553,8 @@ const InternalInputNumber = React.forwardRef(

// >>> Focus & Blur
const onBlur = () => {
imeCompositionRef.current = false;

if (changeOnBlur) {
flushInputValue(false);
}
66 changes: 66 additions & 0 deletions tests/formatter.test.tsx
Original file line number Diff line number Diff line change
@@ -63,6 +63,72 @@ describe('InputNumber.Formatter', () => {
expect(onChange).toHaveBeenCalledWith(100);
});

it('formatter on IME numeric keypad input', () => {
const { container } = render(
<InputNumber formatter={(value) => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} />
);
const input = container.querySelector('input');
fireEvent.focus(input);
fireEvent.compositionStart(input);
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Numpad1',
keyCode: 229,
composed: true,
});
fireEvent.change(input, { target: { value: '1' } });
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Numpad2',
keyCode: 229,
composed: true,
});
fireEvent.change(input, { target: { value: '12' } });
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Numpad3',
keyCode: 229,
composed: true,
});
fireEvent.change(input, { target: { value: '123' } });
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Numpad4',
keyCode: 229,
composed: true,
});
fireEvent.change(input, { target: { value: '1234' } });
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Enter',
keyCode: 229,
composed: true,
});
fireEvent.compositionEnd(input);
fireEvent.blur(input);
expect(input.value).toEqual('1,234');

fireEvent.focus(input);
fireEvent.compositionStart(input);
fireEvent.keyDown(input, {
which: 229,
key: 'Process',
code: 'Numpad5',
keyCode: 229,
composed: true,
});
fireEvent.change(input, { target: { value: '12345' } });
fireEvent.compositionEnd(input);
fireEvent.change(input, { target: { value: '1234' } });
fireEvent.blur(input);
expect(input.value).toEqual('12,345');
});

it('formatter and parser', () => {
const onChange = jest.fn();
const { container } = render(