Skip to content

Commit 0d7cc92

Browse files
perf(TextInput): skip redundant character counter updates (#7600)
Co-authored-by: primer[bot] <119360173+primer[bot]@users.noreply.github.com>
1 parent 17ec46f commit 0d7cc92

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
perf(TextInput): skip redundant character counter updates

packages/react/src/TextInput/TextInput.tsx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
108108
const [isOverLimit, setIsOverLimit] = useState<boolean>(false)
109109
const [screenReaderMessage, setScreenReaderMessage] = useState<string>('')
110110
const characterCounterRef = useRef<CharacterCounter | null>(null)
111+
const lastCountedLengthRef = useRef<number | null>(null)
112+
const lastCharacterCountRef = useRef<string>('')
113+
const lastIsOverLimitRef = useRef<boolean>(false)
114+
const lastScreenReaderMessageRef = useRef<string>('')
111115

112116
// this class is necessary to style FilterSearch, plz no touchy!
113117
const wrapperClasses = clsx(className, 'TextInput-wrapper')
@@ -157,17 +161,33 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
157161
if (characterLimit) {
158162
characterCounterRef.current = new CharacterCounter({
159163
onCountUpdate: (count, overLimit, message) => {
160-
setCharacterCount(message)
161-
setIsOverLimit(overLimit)
164+
if (message !== lastCharacterCountRef.current) {
165+
lastCharacterCountRef.current = message
166+
setCharacterCount(message)
167+
}
168+
169+
if (overLimit !== lastIsOverLimitRef.current) {
170+
lastIsOverLimitRef.current = overLimit
171+
setIsOverLimit(overLimit)
172+
}
162173
},
163174
onScreenReaderAnnounce: message => {
164-
setScreenReaderMessage(message)
175+
if (message !== lastScreenReaderMessageRef.current) {
176+
lastScreenReaderMessageRef.current = message
177+
setScreenReaderMessage(message)
178+
}
165179
},
166180
})
167181

182+
lastCountedLengthRef.current = null
183+
168184
return () => {
169185
characterCounterRef.current?.cleanup()
170186
characterCounterRef.current = null
187+
lastCountedLengthRef.current = null
188+
lastCharacterCountRef.current = ''
189+
lastIsOverLimitRef.current = false
190+
lastScreenReaderMessageRef.current = ''
171191
}
172192
}
173193
}, [characterLimit])
@@ -177,15 +197,25 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
177197
if (characterLimit && characterCounterRef.current) {
178198
const currentValue =
179199
value !== undefined ? String(value) : defaultValue !== undefined ? String(defaultValue) : ''
180-
characterCounterRef.current.updateCharacterCount(currentValue.length, characterLimit)
200+
const currentLength = currentValue.length
201+
202+
if (currentLength !== lastCountedLengthRef.current) {
203+
lastCountedLengthRef.current = currentLength
204+
characterCounterRef.current.updateCharacterCount(currentLength, characterLimit)
205+
}
181206
}
182207
}, [value, defaultValue, characterLimit])
183208

184209
// Handle input change with character counter
185210
const handleInputChange = useCallback(
186211
(e: React.ChangeEvent<HTMLInputElement>) => {
187212
if (characterLimit && characterCounterRef.current) {
188-
characterCounterRef.current.updateCharacterCount(e.target.value.length, characterLimit)
213+
const currentLength = e.target.value.length
214+
215+
if (currentLength !== lastCountedLengthRef.current) {
216+
lastCountedLengthRef.current = currentLength
217+
characterCounterRef.current.updateCharacterCount(currentLength, characterLimit)
218+
}
189219
}
190220
onChange?.(e)
191221
},

0 commit comments

Comments
 (0)