Skip to content

Commit 3aba007

Browse files
authored
fix: trigger onChange twice when inputting using the input method (#61)
* fix: trigger onChange twice when inputting using the input method * chore: code style
1 parent 9640649 commit 3aba007

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

src/Input.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, {
1010
} from 'react';
1111
import BaseInput from './BaseInput';
1212
import useCount from './hooks/useCount';
13-
import type { InputProps, InputRef } from './interface';
13+
import type { ChangeEventInfo, InputProps, InputRef } from './interface';
1414
import type { InputFocusOptions } from './utils/commonUtils';
1515
import { resolveOnChange, triggerFocus } from './utils/commonUtils';
1616

@@ -40,7 +40,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
4040
} = props;
4141

4242
const [focused, setFocused] = useState<boolean>(false);
43-
const compositionRef = React.useRef(false);
43+
const compositionRef = useRef(false);
4444

4545
const inputRef = useRef<HTMLInputElement>(null);
4646

@@ -58,7 +58,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
5858
value === undefined || value === null ? '' : String(value);
5959

6060
// =================== Select Range ===================
61-
const [selection, setSelection] = React.useState<
61+
const [selection, setSelection] = useState<
6262
[start: number, end: number] | null
6363
>(null);
6464

@@ -97,6 +97,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
9797
| React.ChangeEvent<HTMLInputElement>
9898
| React.CompositionEvent<HTMLInputElement>,
9999
currentValue: string,
100+
info: ChangeEventInfo,
100101
) => {
101102
let cutValue = currentValue;
102103

@@ -116,6 +117,10 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
116117
inputRef.current?.selectionEnd || 0,
117118
]);
118119
}
120+
} else if (info.source === 'compositionEnd') {
121+
// Avoid triggering twice
122+
// https://github.com/ant-design/ant-design/issues/46587
123+
return;
119124
}
120125
setValue(cutValue);
121126

@@ -124,21 +129,25 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
124129
}
125130
};
126131

127-
React.useEffect(() => {
132+
useEffect(() => {
128133
if (selection) {
129134
inputRef.current?.setSelectionRange(...selection);
130135
}
131136
}, [selection]);
132137

133138
const onInternalChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
134-
triggerChange(e, e.target.value);
139+
triggerChange(e, e.target.value, {
140+
source: 'change',
141+
});
135142
};
136143

137144
const onInternalCompositionEnd = (
138145
e: React.CompositionEvent<HTMLInputElement>,
139146
) => {
140147
compositionRef.current = false;
141-
triggerChange(e, e.currentTarget.value);
148+
triggerChange(e, e.currentTarget.value, {
149+
source: 'compositionEnd',
150+
});
142151
onCompositionEnd?.(e);
143152
};
144153

src/interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,7 @@ export interface InputRef {
149149
select: () => void;
150150
input: HTMLInputElement | null;
151151
}
152+
153+
export interface ChangeEventInfo {
154+
source: 'compositionEnd' | 'change';
155+
}

tests/count.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,45 @@ describe('Input.Count', () => {
109109
expect(setSelectionRange).toHaveBeenCalledWith(2, 2);
110110
});
111111

112+
it('input using the input method should trigger onChange once', () => {
113+
const onChange = jest.fn();
114+
const { container } = render(<Input onChange={onChange} />);
115+
const input = container.querySelector('input')!;
116+
fireEvent.compositionStart(input);
117+
fireEvent.compositionUpdate(input, { data: '你' });
118+
fireEvent.compositionEnd(input, { data: '你好' });
119+
fireEvent.input(input, { target: { value: '你好' } });
120+
expect(onChange).toHaveBeenCalledTimes(1);
121+
});
122+
123+
it('using the input method to enter the cropped content should trigger onChange twice', () => {
124+
const onChange = jest.fn();
125+
const onCompositionEnd = jest.fn();
126+
const { container } = render(
127+
<Input
128+
count={{
129+
show: true,
130+
max: 3,
131+
exceedFormatter: (val, { max }) =>
132+
getSegments(val)
133+
.filter((seg) => seg.index + seg.segment.length <= max)
134+
.map((seg) => seg.segment)
135+
.join(''),
136+
}}
137+
onChange={onChange}
138+
onCompositionEnd={onCompositionEnd}
139+
/>,
140+
);
141+
const input = container.querySelector('input')!;
142+
fireEvent.compositionStart(input);
143+
fireEvent.compositionUpdate(input, { target: { value: '你' } });
144+
fireEvent.compositionUpdate(input, { target: { value: '你好' } });
145+
fireEvent.compositionUpdate(input, { target: { value: '你好世' } });
146+
fireEvent.compositionUpdate(input, { target: { value: '你好世界' } });
147+
fireEvent.compositionEnd(input, { target: { value: '你好世界' } });
148+
expect(input?.value).toEqual('你好世');
149+
});
150+
112151
describe('cls', () => {
113152
it('raw', () => {
114153
const { container } = render(

0 commit comments

Comments
 (0)