From 92486ac7872a7bf9f36b7d7672d69cf52fda5198 Mon Sep 17 00:00:00 2001 From: RainyLiao Date: Mon, 25 Dec 2023 15:02:37 +0800 Subject: [PATCH 1/4] fix: trigger onChange twice when inputting using the input method --- src/Input.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Input.tsx b/src/Input.tsx index abce2a9..5be23dd 100644 --- a/src/Input.tsx +++ b/src/Input.tsx @@ -68,6 +68,14 @@ const Input = forwardRef((props, ref) => { const valueLength = countConfig.strategy(formatValue); const isOutOfRange = !!mergedMax && valueLength > mergedMax; + const isExceed = (currentValue: string) => { + return ( + !compositionRef.current && + countConfig.exceedFormatter && + countConfig.max && + countConfig.strategy(currentValue) > countConfig.max + ); + }; // ======================= Ref ======================== useImperativeHandle(ref, () => ({ @@ -100,14 +108,9 @@ const Input = forwardRef((props, ref) => { ) => { let cutValue = currentValue; - if ( - !compositionRef.current && - countConfig.exceedFormatter && - countConfig.max && - countConfig.strategy(currentValue) > countConfig.max - ) { - cutValue = countConfig.exceedFormatter(currentValue, { - max: countConfig.max, + if (isExceed(currentValue)) { + cutValue = countConfig.exceedFormatter!(currentValue, { + max: countConfig.max!, }); if (currentValue !== cutValue) { @@ -138,7 +141,8 @@ const Input = forwardRef((props, ref) => { e: React.CompositionEvent, ) => { compositionRef.current = false; - triggerChange(e, e.currentTarget.value); + if (isExceed(e.currentTarget.value)) + triggerChange(e, e.currentTarget.value); onCompositionEnd?.(e); }; From 2b01a69615c8a6f39e2b8f95ebf6ff9af9c3012c Mon Sep 17 00:00:00 2001 From: RainyLiao Date: Mon, 25 Dec 2023 21:58:01 +0800 Subject: [PATCH 2/4] style: if statement wrapped in braces --- src/Input.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Input.tsx b/src/Input.tsx index 5be23dd..39d11e2 100644 --- a/src/Input.tsx +++ b/src/Input.tsx @@ -141,8 +141,9 @@ const Input = forwardRef((props, ref) => { e: React.CompositionEvent, ) => { compositionRef.current = false; - if (isExceed(e.currentTarget.value)) + if (isExceed(e.currentTarget.value)) { triggerChange(e, e.currentTarget.value); + } onCompositionEnd?.(e); }; From 01f47bab3b8fda639db152068d0ca919c63a3dbf Mon Sep 17 00:00:00 2001 From: RainyLiao Date: Mon, 25 Dec 2023 21:58:55 +0800 Subject: [PATCH 3/4] test: add test cases for onChange when input method combination is input --- tests/count.test.tsx | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/count.test.tsx b/tests/count.test.tsx index a6c22e4..ccee16e 100644 --- a/tests/count.test.tsx +++ b/tests/count.test.tsx @@ -109,6 +109,45 @@ describe('Input.Count', () => { expect(setSelectionRange).toHaveBeenCalledWith(2, 2); }); + it('input using the input method should trigger onChange once', () => { + const onChange = jest.fn(); + const { container } = render(); + const input = container.querySelector('input')!; + fireEvent.compositionStart(input); + fireEvent.compositionUpdate(input, { data: '你' }); + fireEvent.compositionEnd(input, { data: '你好' }); + fireEvent.input(input, { target: { value: '你好' } }); + expect(onChange).toHaveBeenCalledTimes(1); + }); + + it('using the input method to enter the cropped content should trigger onChange twice', () => { + const onChange = jest.fn(); + const onCompositionEnd = jest.fn(); + const { container } = render( + + getSegments(val) + .filter((seg) => seg.index + seg.segment.length <= max) + .map((seg) => seg.segment) + .join(''), + }} + onChange={onChange} + onCompositionEnd={onCompositionEnd} + />, + ); + const input = container.querySelector('input')!; + fireEvent.compositionStart(input); + fireEvent.compositionUpdate(input, { target: { value: '你' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好世' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好世界' } }); + fireEvent.compositionEnd(input, { target: { value: '你好世界' } }); + expect(input?.value).toEqual('你好世'); + }); + describe('cls', () => { it('raw', () => { const { container } = render( From a8a9b4ead66bba9f1565744de3ce68bbb270ddc7 Mon Sep 17 00:00:00 2001 From: RainyLiao Date: Tue, 26 Dec 2023 12:05:50 +0800 Subject: [PATCH 4/4] perf: extracts the logic of the formatter, the condition calls triggerChange --- src/Input.tsx | 55 ++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/Input.tsx b/src/Input.tsx index 39d11e2..5b7aa82 100644 --- a/src/Input.tsx +++ b/src/Input.tsx @@ -68,14 +68,6 @@ const Input = forwardRef((props, ref) => { const valueLength = countConfig.strategy(formatValue); const isOutOfRange = !!mergedMax && valueLength > mergedMax; - const isExceed = (currentValue: string) => { - return ( - !compositionRef.current && - countConfig.exceedFormatter && - countConfig.max && - countConfig.strategy(currentValue) > countConfig.max - ); - }; // ======================= Ref ======================== useImperativeHandle(ref, () => ({ @@ -106,13 +98,31 @@ const Input = forwardRef((props, ref) => { | React.CompositionEvent, currentValue: string, ) => { - let cutValue = currentValue; + setValue(currentValue); + if (inputRef.current) { + resolveOnChange(inputRef.current, e, onChange, currentValue); + } + }; + + React.useEffect(() => { + if (selection) { + inputRef.current?.setSelectionRange(...selection); + } + }, [selection]); - if (isExceed(currentValue)) { + const onInternalFormatter = (currentValue: string) => { + let cutValue = currentValue; + let isExceed = false; + if ( + !compositionRef.current && + countConfig.exceedFormatter && + countConfig.max && + countConfig.strategy(currentValue) > countConfig.max + ) { + isExceed = true; cutValue = countConfig.exceedFormatter!(currentValue, { max: countConfig.max!, }); - if (currentValue !== cutValue) { setSelection([ inputRef.current?.selectionStart || 0, @@ -120,29 +130,24 @@ const Input = forwardRef((props, ref) => { ]); } } - setValue(cutValue); - - if (inputRef.current) { - resolveOnChange(inputRef.current, e, onChange, cutValue); - } + return { + cutValue, + isExceed, + }; }; - React.useEffect(() => { - if (selection) { - inputRef.current?.setSelectionRange(...selection); - } - }, [selection]); - const onInternalChange: React.ChangeEventHandler = (e) => { - triggerChange(e, e.target.value); + const { cutValue } = onInternalFormatter(e.target.value); + triggerChange(e, cutValue); }; const onInternalCompositionEnd = ( e: React.CompositionEvent, ) => { compositionRef.current = false; - if (isExceed(e.currentTarget.value)) { - triggerChange(e, e.currentTarget.value); + const { cutValue, isExceed } = onInternalFormatter(e.currentTarget.value); + if (isExceed) { + triggerChange(e, cutValue); } onCompositionEnd?.(e); };