diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 164c09dc764..5cebe10a3ec 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -449,6 +449,12 @@ function InternalTextInput(props: TextInputProps): React.Node { before we can get to the long term breaking change. */ if (instance != null) { + // Register the input immediately when the ref is set so that focus() + // can be called from ref callbacks + // Double registering during useLayoutEffect is fine, because the underlying + // state is a Set. + TextInputState.registerInput(instance); + // $FlowFixMe[prop-missing] - See the explanation above. // $FlowFixMe[unsafe-object-assign] Object.assign(instance, { diff --git a/packages/react-native/Libraries/Components/View/ViewNativeComponent.js b/packages/react-native/Libraries/Components/View/ViewNativeComponent.js index 192456169e7..600eee6fedb 100644 --- a/packages/react-native/Libraries/Components/View/ViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/View/ViewNativeComponent.js @@ -21,8 +21,8 @@ const ViewNativeComponent: HostComponent = })); interface NativeCommands { - +focus: () => void; - +blur: () => void; + +focus: (viewRef: HostInstance) => void; + +blur: (viewRef: HostInstance) => void; +hotspotUpdate: (viewRef: HostInstance, x: number, y: number) => void; +setPressed: (viewRef: HostInstance, pressed: boolean) => void; } diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js index 1d0543d14c7..dcd1cd1df25 100644 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/__tests__/ReactFabricPublicInstance-itest.js @@ -14,6 +14,7 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; import type {HostInstance} from 'react-native'; import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement'; +import TextInput from '../../../Components/TextInput/TextInput'; import TextInputState from '../../../Components/TextInput/TextInputState'; import View from '../../../Components/View/View'; import ReactFabricHostComponent from '../ReactFabricHostComponent'; @@ -48,7 +49,7 @@ describe('ReactFabricPublicInstance', () => { const nodeRef = createRef(); Fantom.runTask(() => { - root.render(); + root.render(); }); const node = nullthrows(nodeRef.current); @@ -73,7 +74,7 @@ describe('ReactFabricPublicInstance', () => { const ref = createRef(); Fantom.runTask(() => { - root.render(); + root.render(); }); const node = nullthrows(ref.current); diff --git a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js index 3da3ed4484a..78ab5696f43 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js +++ b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js @@ -25,8 +25,10 @@ import type {InstanceHandle} from './internals/NodeInternals'; import type ReactNativeDocument from './ReactNativeDocument'; import TextInputState from '../../../../../Libraries/Components/TextInput/TextInputState'; +import {Commands as ViewCommands} from '../../../../../Libraries/Components/View/ViewNativeComponent'; import {create as createAttributePayload} from '../../../../../Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload'; import warnForStyleProps from '../../../../../Libraries/ReactNative/ReactFabricPublicInstance/warnForStyleProps'; +import * as ReactNativeFeatureFlags from '../../../featureflags/ReactNativeFeatureFlags'; import { getNativeElementReference, getPublicInstanceFromInstanceHandle, @@ -140,11 +142,19 @@ class ReactNativeElement extends ReadOnlyElement implements NativeMethods { */ blur(): void { - TextInputState.blurTextInput(this); + if (TextInputState.isTextInput(this)) { + TextInputState.blurTextInput(this); + } else if (ReactNativeFeatureFlags.enableImperativeFocus()) { + ViewCommands.blur(this); + } } focus() { - TextInputState.focusTextInput(this); + if (TextInputState.isTextInput(this)) { + TextInputState.focusTextInput(this); + } else if (ReactNativeFeatureFlags.enableImperativeFocus()) { + ViewCommands.focus(this); + } } measure(callback: MeasureOnSuccessCallback) {