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
This commit is contained in:
Samuel Susla 2021-12-30 06:37:32 -08:00 коммит произвёл Facebook GitHub Bot
Родитель f3d0a67a73
Коммит 8c6a98400e
9 изменённых файлов: 72 добавлений и 2 удалений

Просмотреть файл

@ -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
@ -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,
@ -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);
@ -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}
@ -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}

Просмотреть файл

@ -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]}
@ -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]}

Просмотреть файл

@ -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;

Просмотреть файл

@ -62,6 +62,7 @@ RCT_EXPORT_VIEW_PROPERTY(passwordRules, NSString)
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)

Просмотреть файл

@ -381,7 +381,13 @@ using namespace facebook::react;
[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]);
}
}
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -99,6 +99,12 @@ TextInputProps::TextInputProps(
rawProps,
"onKeyPressSync",
sourceProps.onKeyPressSync,
{})),
onChangeSync(convertRawProp(
context,
rawProps,
"onChangeSync",
sourceProps.onChangeSync,
{})){};
TextAttributes TextInputProps::getEffectiveTextAttributes(

Просмотреть файл

@ -63,6 +63,7 @@ class TextInputProps final : public ViewProps, public BaseTextProps {
std::string const inputAccessoryViewID{};
bool onKeyPressSync{false};
bool onChangeSync{false};
/*
* Accessors