Skip to content

Commit 8c6a984

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Introduce TextInput.onChangeSync
Summary: changelog: [internal] Add experimental `TextInput.onChangeSync` which delivers onChange event synchronously. Reviewed By: ShikaSD Differential Revision: D33188083 fbshipit-source-id: 1e1dcd0d71c7eec98d3d5f69967659e07ac4e6a6
1 parent f3d0a67 commit 8c6a984

File tree

9 files changed

+72
-2
lines changed

9 files changed

+72
-2
lines changed

Diff for: Libraries/Components/TextInput/TextInput.js

+47-1
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,33 @@ export type Props = $ReadOnly<{|
598598
*/
599599
onChange?: ?(e: ChangeEvent) => mixed,
600600

601+
/**
602+
* DANGER: this API is not stable and will change in the future.
603+
*
604+
* Callback will be called on the main thread and may result in dropped frames.
605+
* Callback that is called when the text input's text changes.
606+
*
607+
* @platform ios
608+
*/
609+
unstable_onChangeSync?: ?(e: ChangeEvent) => mixed,
610+
601611
/**
602612
* Callback that is called when the text input's text changes.
603613
* Changed text is passed as an argument to the callback handler.
604614
*/
605615
onChangeText?: ?(text: string) => mixed,
606616

617+
/**
618+
* DANGER: this API is not stable and will change in the future.
619+
*
620+
* Callback will be called on the main thread and may result in dropped frames.
621+
* Callback that is called when the text input's text changes.
622+
* Changed text is passed as an argument to the callback handler.
623+
*
624+
* @platform ios
625+
*/
626+
unstable_onChangeTextSync?: ?(text: string) => mixed,
627+
607628
/**
608629
* Callback that is called when the text input's content size changes.
609630
* This will be called with
@@ -643,7 +664,7 @@ export type Props = $ReadOnly<{|
643664
* the typed-in character otherwise including `' '` for space.
644665
* Fires before `onChange` callbacks.
645666
*
646-
* Only available in Fabric on iOS.
667+
* @platform ios
647668
*/
648669
unstable_onKeyPressSync?: ?(e: KeyPressEvent) => mixed,
649670

@@ -1101,6 +1122,26 @@ function InternalTextInput(props: Props): React.Node {
11011122
setMostRecentEventCount(event.nativeEvent.eventCount);
11021123
};
11031124

1125+
const _onChangeSync = (event: ChangeEvent) => {
1126+
const currentText = event.nativeEvent.text;
1127+
props.unstable_onChangeSync && props.unstable_onChangeSync(event);
1128+
props.unstable_onChangeTextSync &&
1129+
props.unstable_onChangeTextSync(currentText);
1130+
1131+
if (inputRef.current == null) {
1132+
// calling `props.onChange` or `props.onChangeText`
1133+
// may clean up the input itself. Exits here.
1134+
return;
1135+
}
1136+
1137+
setLastNativeText(currentText);
1138+
// This must happen last, after we call setLastNativeText.
1139+
// Different ordering can cause bugs when editing AndroidTextInputs
1140+
// with multiple Fragments.
1141+
// We must update this so that controlled input updates work.
1142+
setMostRecentEventCount(event.nativeEvent.eventCount);
1143+
};
1144+
11041145
const _onSelectionChange = (event: SelectionChangeEvent) => {
11051146
props.onSelectionChange && props.onSelectionChange(event);
11061147

@@ -1187,6 +1228,10 @@ function InternalTextInput(props: Props): React.Node {
11871228
? [styles.multilineInput, props.style]
11881229
: props.style;
11891230

1231+
const useOnChangeSync =
1232+
(props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
1233+
!(props.onChange || props.onChangeText);
1234+
11901235
textInput = (
11911236
<RCTTextInputView
11921237
ref={_setNativeRef}
@@ -1201,6 +1246,7 @@ function InternalTextInput(props: Props): React.Node {
12011246
onBlur={_onBlur}
12021247
onKeyPressSync={props.unstable_onKeyPressSync}
12031248
onChange={_onChange}
1249+
onChangeSync={useOnChangeSync === true ? _onChangeSync : null}
12041250
onContentSizeChange={props.onContentSizeChange}
12051251
onFocus={_onFocus}
12061252
onScroll={_onScroll}

Diff for: Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ exports[`TextInput tests should render as expected: should deep render when mock
1010
mostRecentEventCount={0}
1111
onBlur={[Function]}
1212
onChange={[Function]}
13+
onChangeSync={null}
1314
onClick={[Function]}
1415
onFocus={[Function]}
1516
onResponderGrant={[Function]}
@@ -38,6 +39,7 @@ exports[`TextInput tests should render as expected: should deep render when not
3839
mostRecentEventCount={0}
3940
onBlur={[Function]}
4041
onChange={[Function]}
42+
onChangeSync={null}
4143
onClick={[Function]}
4244
onFocus={[Function]}
4345
onResponderGrant={[Function]}

Diff for: Libraries/Text/TextInput/RCTBaseTextInputView.h

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
3535
@property (nonatomic, copy, nullable) RCTDirectEventBlock onContentSizeChange;
3636
@property (nonatomic, copy, nullable) RCTDirectEventBlock onSelectionChange;
3737
@property (nonatomic, copy, nullable) RCTDirectEventBlock onChange;
38+
@property (nonatomic, copy, nullable) RCTDirectEventBlock onChangeSync;
3839
@property (nonatomic, copy, nullable) RCTDirectEventBlock onTextInput;
3940
@property (nonatomic, copy, nullable) RCTDirectEventBlock onScroll;
4041

Diff for: Libraries/Text/TextInput/RCTBaseTextInputViewManager.m

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ @implementation RCTBaseTextInputViewManager
6262

6363
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
6464
RCT_EXPORT_VIEW_PROPERTY(onKeyPressSync, RCTDirectEventBlock)
65+
RCT_EXPORT_VIEW_PROPERTY(onChangeSync, RCTDirectEventBlock)
6566
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
6667
RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock)
6768
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)

Diff for: React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

+7-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,13 @@ - (void)textInputDidChange
381381
[self _updateState];
382382

383383
if (_eventEmitter) {
384-
std::static_pointer_cast<TextInputEventEmitter const>(_eventEmitter)->onChange([self _textInputMetrics]);
384+
auto const &textInputEventEmitter = *std::static_pointer_cast<TextInputEventEmitter const>(_eventEmitter);
385+
auto const &props = *std::static_pointer_cast<TextInputProps const>(_props);
386+
if (props.onChangeSync) {
387+
textInputEventEmitter.onChangeSync([self _textInputMetrics]);
388+
} else {
389+
textInputEventEmitter.onChange([self _textInputMetrics]);
390+
}
385391
}
386392
}
387393

Diff for: ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ void TextInputEventEmitter::onChange(
7575
dispatchTextInputEvent("change", textInputMetrics);
7676
}
7777

78+
void TextInputEventEmitter::onChangeSync(
79+
TextInputMetrics const &textInputMetrics) const {
80+
dispatchTextInputEvent(
81+
"changeSync", textInputMetrics, EventPriority::SynchronousBatched);
82+
}
83+
7884
void TextInputEventEmitter::onContentSizeChange(
7985
TextInputMetrics const &textInputMetrics) const {
8086
dispatchTextInputEvent("contentSizeChange", textInputMetrics);

Diff for: ReactCommon/react/renderer/components/textinput/iostextinput/TextInputEventEmitter.h

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
4040
void onFocus(TextInputMetrics const &textInputMetrics) const;
4141
void onBlur(TextInputMetrics const &textInputMetrics) const;
4242
void onChange(TextInputMetrics const &textInputMetrics) const;
43+
void onChangeSync(TextInputMetrics const &textInputMetrics) const;
4344
void onContentSizeChange(TextInputMetrics const &textInputMetrics) const;
4445
void onSelectionChange(TextInputMetrics const &textInputMetrics) const;
4546
void onEndEditing(TextInputMetrics const &textInputMetrics) const;

Diff for: ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ TextInputProps::TextInputProps(
9999
rawProps,
100100
"onKeyPressSync",
101101
sourceProps.onKeyPressSync,
102+
{})),
103+
onChangeSync(convertRawProp(
104+
context,
105+
rawProps,
106+
"onChangeSync",
107+
sourceProps.onChangeSync,
102108
{})){};
103109

104110
TextAttributes TextInputProps::getEffectiveTextAttributes(

Diff for: ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class TextInputProps final : public ViewProps, public BaseTextProps {
6363
std::string const inputAccessoryViewID{};
6464

6565
bool onKeyPressSync{false};
66+
bool onChangeSync{false};
6667

6768
/*
6869
* Accessors

0 commit comments

Comments
 (0)