diff --git a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js index 09b88e8e96c..e8598a6bfc4 100644 --- a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +++ b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js @@ -24,6 +24,7 @@ import type {TextStyleProp, ViewStyleProp} from '../../StyleSheet/StyleSheet'; import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; import requireNativeComponent from '../../ReactNative/requireNativeComponent'; import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; +import type {TextInputNativeCommands} from './TextInputNativeCommands'; import * as React from 'react'; import AndroidTextInputViewConfig from './AndroidTextInputViewConfig'; const ReactNativeViewConfigRegistry = require('../../Renderer/shims/ReactNativeViewConfigRegistry'); @@ -540,21 +541,7 @@ export type NativeProps = $ReadOnly<{| type NativeType = HostComponent; -interface NativeCommands { - +focus: (viewRef: React.ElementRef) => void; - +blur: (viewRef: React.ElementRef) => void; - +setMostRecentEventCount: ( - viewRef: React.ElementRef, - eventCount: Int32, - ) => void; - +setTextAndSelection: ( - viewRef: React.ElementRef, - mostRecentEventCount: Int32, - value: ?string, // in theory this is nullable - start: Int32, - end: Int32, - ) => void; -} +type NativeCommands = TextInputNativeCommands; export const Commands: NativeCommands = codegenNativeCommands({ supportedCommands: [ diff --git a/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js b/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js index e4ba8288daa..950e5e1215c 100644 --- a/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js +++ b/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js @@ -14,25 +14,12 @@ import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; import requireNativeComponent from '../../ReactNative/requireNativeComponent'; import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; import type {Int32} from '../../Types/CodegenTypes'; +import type {TextInputNativeCommands} from './TextInputNativeCommands'; import * as React from 'react'; type NativeType = HostComponent; -interface NativeCommands { - +focus: (viewRef: React.ElementRef) => void; - +blur: (viewRef: React.ElementRef) => void; - +setMostRecentEventCount: ( - viewRef: React.ElementRef, - eventCount: Int32, - ) => void; - +setTextAndSelection: ( - viewRef: React.ElementRef, - mostRecentEventCount: Int32, - value: ?string, // in theory this is nullable - start: Int32, - end: Int32, - ) => void; -} +type NativeCommands = TextInputNativeCommands; export const Commands: NativeCommands = codegenNativeCommands({ supportedCommands: [ diff --git a/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js b/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js index 4333854ce42..78fcf530262 100644 --- a/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js +++ b/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js @@ -15,26 +15,13 @@ import requireNativeComponent from '../../ReactNative/requireNativeComponent'; import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; import type {Int32} from '../../Types/CodegenTypes'; import * as React from 'react'; +import type {TextInputNativeCommands} from './TextInputNativeCommands'; import RCTSinglelineTextInputViewConfig from './RCTSinglelineTextInputViewConfig'; const ReactNativeViewConfigRegistry = require('../../Renderer/shims/ReactNativeViewConfigRegistry'); type NativeType = HostComponent; -interface NativeCommands { - +focus: (viewRef: React.ElementRef) => void; - +blur: (viewRef: React.ElementRef) => void; - +setMostRecentEventCount: ( - viewRef: React.ElementRef, - eventCount: Int32, - ) => void; - +setTextAndSelection: ( - viewRef: React.ElementRef, - mostRecentEventCount: Int32, - value: ?string, // in theory this is nullable - start: Int32, - end: Int32, - ) => void; -} +type NativeCommands = TextInputNativeCommands; export const Commands: NativeCommands = codegenNativeCommands({ supportedCommands: [ diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 35b281339ae..478af12c4c4 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -30,6 +30,7 @@ import type {ViewProps} from '../View/ViewPropTypes'; import type {SyntheticEvent, ScrollEvent} from '../../Types/CoreEventTypes'; import type {PressEvent} from '../../Types/CoreEventTypes'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; +import type {TextInputNativeCommands} from './TextInputNativeCommands'; const {useEffect, useRef, useState} = React; @@ -37,18 +38,24 @@ type ReactRefSetter = {current: null | T, ...} | ((ref: null | T) => mixed); let AndroidTextInput; let AndroidTextInputCommands; -let RCTMultilineTextInputView; let RCTSinglelineTextInputView; +let RCTSinglelineTextInputNativeCommands; +let RCTMultilineTextInputView; +let RCTMultilineTextInputNativeCommands; if (Platform.OS === 'android') { AndroidTextInput = require('./AndroidTextInputNativeComponent').default; AndroidTextInputCommands = require('./AndroidTextInputNativeComponent') .Commands; } else if (Platform.OS === 'ios') { - RCTMultilineTextInputView = require('./RCTMultilineTextInputNativeComponent') - .default; RCTSinglelineTextInputView = require('./RCTSingelineTextInputNativeComponent') .default; + RCTSinglelineTextInputNativeCommands = require('./RCTSingelineTextInputNativeComponent') + .Commands; + RCTMultilineTextInputView = require('./RCTMultilineTextInputNativeComponent') + .default; + RCTMultilineTextInputNativeCommands = require('./RCTMultilineTextInputNativeComponent') + .Commands; } export type ChangeEvent = SyntheticEvent< @@ -845,6 +852,15 @@ function InternalTextInput(props: Props): React.Node { selection = null; } + let viewCommands: TextInputNativeCommands>; + if (AndroidTextInputCommands) { + viewCommands = AndroidTextInputCommands; + } else { + viewCommands = props.multiline + ? RCTMultilineTextInputNativeCommands + : RCTSinglelineTextInputNativeCommands; + } + const text = typeof props.value === 'string' ? props.value @@ -877,16 +893,14 @@ function InternalTextInput(props: Props): React.Node { return; } - if (AndroidTextInputCommands && inputRef.current != null) { - AndroidTextInputCommands.setTextAndSelection( + if (inputRef.current != null) { + viewCommands.setTextAndSelection( inputRef.current, mostRecentEventCount, text, selection?.start ?? -1, selection?.end ?? -1, ); - } else if (inputRef.current != null) { - inputRef.current.setNativeProps(nativeUpdate); } }, [ mostRecentEventCount, @@ -897,6 +911,7 @@ function InternalTextInput(props: Props): React.Node { selection, lastNativeSelection, text, + viewCommands, ]); useEffect(() => { @@ -921,16 +936,14 @@ function InternalTextInput(props: Props): React.Node { }, [inputRef]); function clear(): void { - if (AndroidTextInputCommands && inputRef.current != null) { - AndroidTextInputCommands.setTextAndSelection( + if (inputRef.current != null) { + viewCommands.setTextAndSelection( inputRef.current, mostRecentEventCount, '', 0, 0, ); - } else if (inputRef.current != null) { - inputRef.current.setNativeProps({text: ''}); } } @@ -985,17 +998,6 @@ function InternalTextInput(props: Props): React.Node { }; const _onChange = (event: ChangeEvent) => { - if (AndroidTextInputCommands && inputRef.current != null) { - // Do nothing - } else if (inputRef.current != null) { - // Make sure to fire the mostRecentEventCount first so it is already set on - // native when the text value is set. - // This is now only relevant on iOS until we migrate to ViewCommands everywhere - inputRef.current.setNativeProps({ - mostRecentEventCount: event.nativeEvent.eventCount, - }); - } - const text = event.nativeEvent.text; props.onChange && props.onChange(event); props.onChangeText && props.onChangeText(text); diff --git a/Libraries/Components/TextInput/TextInputNativeCommands.js b/Libraries/Components/TextInput/TextInputNativeCommands.js new file mode 100644 index 00000000000..d669b09f9d1 --- /dev/null +++ b/Libraries/Components/TextInput/TextInputNativeCommands.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import * as React from 'react'; + +import type {Int32} from '../../Types/CodegenTypes'; + +export interface TextInputNativeCommands { + +focus: (viewRef: React.ElementRef) => void; + +blur: (viewRef: React.ElementRef) => void; + +setMostRecentEventCount: ( + viewRef: React.ElementRef, + eventCount: Int32, + ) => void; + +setTextAndSelection: ( + viewRef: React.ElementRef, + mostRecentEventCount: Int32, + value: ?string, // in theory this is nullable + start: Int32, + end: Int32, + ) => void; +} + +const supportedCommands = [ + 'focus', + 'blur', + 'setMostRecentEventCount', + 'setTextAndSelection', +]; + +export default supportedCommands;