Skip to content

Commit 8a7ce55

Browse files
alanleedevfacebook-github-bot
authored andcommitted
Add selection to TextInput onChange event
Summary: This change adds `selection` data to the `TextInput.onChange` event for both iOS and Android platforms. ## Why On the web, text input elements provide `selectionStart` and `selectionEnd` properties that are always accessible during input events. This is a W3C DOM standard that enables common use cases like: - mention and tagging systems (insert at cursor position) - Autocomplete/typeahead (replace text at cursor) - Input masking (format text while maintaining cursor position) - Rich text editing features React Native's `onChange` event previously included `selection` on iOS (Fabric), but this was removed in D76253999 to unify with Android (which never had it). This change restores and extends this capability to both platforms, better aligning React Native with web standards. ## What Changed 1. **Flow Types**: Added optional `selection?: Selection` to `TextInputChangeEventData` 2. **iOS/macOS (C++)**: Enable selection in `onChange` event via `TextInputEventEmitter` 3. **Android (Kotlin)**: Added selection data to `ReactTextChangedEvent` 4. **RNTester**: Updated examples to verify the change ## Changelog [General][Added] - TextInput onChange event now includes selection data on iOS and Android Differential Revision: D89898092
1 parent fef5b84 commit 8a7ce55

File tree

6 files changed

+25
-6
lines changed

6 files changed

+25
-6
lines changed

packages/react-native/Libraries/Components/TextInput/TextInput.flow.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type TextInputChangeEventData = $ReadOnly<{
2929
eventCount: number,
3030
target: number,
3131
text: string,
32+
selection?: Selection,
3233
}>;
3334

3435
export type TextInputChangeEvent =

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextChangedEvent.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ internal class ReactTextChangedEvent(
1717
viewId: Int,
1818
private val text: String,
1919
private val eventCount: Int,
20+
private val selectionStart: Int,
21+
private val selectionEnd: Int,
2022
) : Event<ReactTextChangedEvent>(surfaceId, viewId) {
2123
override fun getEventName(): String = EVENT_NAME
2224

@@ -25,6 +27,12 @@ internal class ReactTextChangedEvent(
2527
putString("text", text)
2628
putInt("eventCount", eventCount)
2729
putInt("target", viewTag)
30+
val selectionData =
31+
Arguments.createMap().apply {
32+
putInt("start", selectionStart)
33+
putInt("end", selectionEnd)
34+
}
35+
putMap("selection", selectionData)
2836
}
2937
}
3038

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputTextWatcher.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ internal class ReactTextInputTextWatcher(
6363
editText.id,
6464
s.toString(),
6565
editText.incrementAndGetEventCounter(),
66+
editText.selectionStart,
67+
editText.selectionEnd,
6668
)
6769
)
6870
}

packages/react-native/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ void TextInputEventEmitter::onBlur(const Metrics& textInputMetrics) const {
140140
}
141141

142142
void TextInputEventEmitter::onChange(const Metrics& textInputMetrics) const {
143-
dispatchTextInputEvent("change", textInputMetrics);
143+
dispatchTextInputEvent("change", textInputMetrics, true);
144144
}
145145

146146
void TextInputEventEmitter::onContentSizeChange(

packages/react-native/ReactNativeApi.d.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<bffba0612c645ddb6166e1f5bb1187f2>>
7+
* @generated SignedSource<<249ca589a73aaef41a965bcf3a2c0208>>
88
*
99
* This file was generated by scripts/js-api/build-types/index.js.
1010
*/
@@ -5277,6 +5277,7 @@ declare type TextInputChangeEvent =
52775277
NativeSyntheticEvent<TextInputChangeEventData>
52785278
declare type TextInputChangeEventData = {
52795279
readonly eventCount: number
5280+
readonly selection?: Selection
52805281
readonly target: number
52815282
readonly text: string
52825283
}
@@ -6189,15 +6190,15 @@ export {
61896190
TaskProvider, // 266dedf2
61906191
Text, // e55ac2e2
61916192
TextContentType, // 239b3ecc
6192-
TextInput, // cf7a3331
6193+
TextInput, // 2e89b91d
61936194
TextInputAndroidProps, // 3f09ce49
6194-
TextInputChangeEvent, // 380cbe93
6195+
TextInputChangeEvent, // 6821f629
61956196
TextInputContentSizeChangeEvent, // 5fba3f54
61966197
TextInputEndEditingEvent, // 8c22fac3
61976198
TextInputFocusEvent, // c36e977c
61986199
TextInputIOSProps, // 0d05a855
61996200
TextInputKeyPressEvent, // 967178c2
6200-
TextInputProps, // a817a7f7
6201+
TextInputProps, // c75f0362
62016202
TextInputSelectionChangeEvent, // a1a7622f
62026203
TextInputSubmitEditingEvent, // 48d903af
62036204
TextLayoutEvent, // 45b0a8d7

packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,14 @@ class TextEventsExample extends React.Component<{...}, $FlowFixMe> {
393393
onFocus={() => this.updateText('onFocus')}
394394
onBlur={() => this.updateText('onBlur')}
395395
onChange={event =>
396-
this.updateText('onChange text: ' + event.nativeEvent.text)
396+
this.updateText(
397+
'onChange text: ' +
398+
event.nativeEvent.text +
399+
', selection: ' +
400+
(event.nativeEvent.selection != null
401+
? JSON.stringify(event.nativeEvent.selection)
402+
: 'undefined'),
403+
)
397404
}
398405
onContentSizeChange={event =>
399406
this.updateText(

0 commit comments

Comments
 (0)