Skip to content

Commit

Permalink
Introduce TextInput.onChangeSync
Browse files Browse the repository at this point in the history
Summary:
changelog: [internal]

Add experimental `TextInput.onChangeSync` which delivers onChange event synchronously.

Reviewed By: ShikaSD

Differential Revision: D33188083

fbshipit-source-id: 1e1dcd0d71c7eec98d3d5f69967659e07ac4e6a6
  • Loading branch information
sammy-SC authored and facebook-github-bot committed Dec 30, 2021
1 parent f3d0a67 commit 8c6a984
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 2 deletions.
48 changes: 47 additions & 1 deletion Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,12 +598,33 @@ export type Props = $ReadOnly<{|
*/
onChange?: ?(e: ChangeEvent) => mixed,

/**
* DANGER: this API is not stable and will change in the future.
*
* Callback will be called on the main thread and may result in dropped frames.
* Callback that is called when the text input's text changes.
*
* @platform ios
*/
unstable_onChangeSync?: ?(e: ChangeEvent) => mixed,

/**
* Callback that is called when the text input's text changes.
* Changed text is passed as an argument to the callback handler.
*/
onChangeText?: ?(text: string) => mixed,

/**
* DANGER: this API is not stable and will change in the future.
*
* Callback will be called on the main thread and may result in dropped frames.
* Callback that is called when the text input's text changes.
* Changed text is passed as an argument to the callback handler.
*
* @platform ios
*/
unstable_onChangeTextSync?: ?(text: string) => mixed,

/**
* Callback that is called when the text input's content size changes.
* This will be called with
Expand Down Expand Up @@ -643,7 +664,7 @@ export type Props = $ReadOnly<{|
* the typed-in character otherwise including `' '` for space.
* Fires before `onChange` callbacks.
*
* Only available in Fabric on iOS.
* @platform ios
*/
unstable_onKeyPressSync?: ?(e: KeyPressEvent) => mixed,

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

const _onChangeSync = (event: ChangeEvent) => {
const currentText = event.nativeEvent.text;
props.unstable_onChangeSync && props.unstable_onChangeSync(event);
props.unstable_onChangeTextSync &&
props.unstable_onChangeTextSync(currentText);

if (inputRef.current == null) {
// calling `props.onChange` or `props.onChangeText`
// may clean up the input itself. Exits here.
return;
}

setLastNativeText(currentText);
// This must happen last, after we call setLastNativeText.
// Different ordering can cause bugs when editing AndroidTextInputs
// with multiple Fragments.
// We must update this so that controlled input updates work.
setMostRecentEventCount(event.nativeEvent.eventCount);
};

const _onSelectionChange = (event: SelectionChangeEvent) => {
props.onSelectionChange && props.onSelectionChange(event);

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

const useOnChangeSync =
(props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
!(props.onChange || props.onChangeText);

textInput = (
<RCTTextInputView
ref={_setNativeRef}
Expand All @@ -1201,6 +1246,7 @@ function InternalTextInput(props: Props): React.Node {
onBlur={_onBlur}
onKeyPressSync={props.unstable_onKeyPressSync}
onChange={_onChange}
onChangeSync={useOnChangeSync === true ? _onChangeSync : null}
onContentSizeChange={props.onContentSizeChange}
onFocus={_onFocus}
onScroll={_onScroll}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports[`TextInput tests should render as expected: should deep render when mock
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
Expand Down Expand Up @@ -38,6 +39,7 @@ exports[`TextInput tests should render as expected: should deep render when not
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBaseTextInputView.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy, nullable) RCTDirectEventBlock onContentSizeChange;
@property (nonatomic, copy, nullable) RCTDirectEventBlock onSelectionChange;
@property (nonatomic, copy, nullable) RCTDirectEventBlock onChange;
@property (nonatomic, copy, nullable) RCTDirectEventBlock onChangeSync;
@property (nonatomic, copy, nullable) RCTDirectEventBlock onTextInput;
@property (nonatomic, copy, nullable) RCTDirectEventBlock onScroll;

Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ @implementation RCTBaseTextInputViewManager

RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onKeyPressSync, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChangeSync, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,13 @@ - (void)textInputDidChange
[self _updateState];

if (_eventEmitter) {
std::static_pointer_cast<TextInputEventEmitter const>(_eventEmitter)->onChange([self _textInputMetrics]);
auto const &textInputEventEmitter = *std::static_pointer_cast<TextInputEventEmitter const>(_eventEmitter);
auto const &props = *std::static_pointer_cast<TextInputProps const>(_props);
if (props.onChangeSync) {
textInputEventEmitter.onChangeSync([self _textInputMetrics]);
} else {
textInputEventEmitter.onChange([self _textInputMetrics]);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ void TextInputEventEmitter::onChange(
dispatchTextInputEvent("change", textInputMetrics);
}

void TextInputEventEmitter::onChangeSync(
TextInputMetrics const &textInputMetrics) const {
dispatchTextInputEvent(
"changeSync", textInputMetrics, EventPriority::SynchronousBatched);
}

void TextInputEventEmitter::onContentSizeChange(
TextInputMetrics const &textInputMetrics) const {
dispatchTextInputEvent("contentSizeChange", textInputMetrics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
void onFocus(TextInputMetrics const &textInputMetrics) const;
void onBlur(TextInputMetrics const &textInputMetrics) const;
void onChange(TextInputMetrics const &textInputMetrics) const;
void onChangeSync(TextInputMetrics const &textInputMetrics) const;
void onContentSizeChange(TextInputMetrics const &textInputMetrics) const;
void onSelectionChange(TextInputMetrics const &textInputMetrics) const;
void onEndEditing(TextInputMetrics const &textInputMetrics) const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ TextInputProps::TextInputProps(
rawProps,
"onKeyPressSync",
sourceProps.onKeyPressSync,
{})),
onChangeSync(convertRawProp(
context,
rawProps,
"onChangeSync",
sourceProps.onChangeSync,
{})){};

TextAttributes TextInputProps::getEffectiveTextAttributes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class TextInputProps final : public ViewProps, public BaseTextProps {
std::string const inputAccessoryViewID{};

bool onKeyPressSync{false};
bool onChangeSync{false};

/*
* Accessors
Expand Down

0 comments on commit 8c6a984

Please sign in to comment.