Skip to content

Commit 6cb3c3d

Browse files
huntiefacebook-github-bot
authored andcommitted
Fix ref type compatibility for builtin components
Summary: Fix `useRef` and `forwardRef` backcompat when migrating to React Native's generated TypeScript types (Strict TypeScript API). Previously this was an awkward breaking change: https://reactnative.dev/docs/strict-typescript-api#some-core-components-are-now-function-components-instead-of-class-components ```diff - const ref = useRef<View>(null); + const ref = useRef<React.ComponentRef<typeof View>>(null); ``` After this diff, no workaround will be required. **Problem detail** In our Flow source code, built-in components like `View` and `TextInput` are function components rather than classes. This differs from the previous manual `.d.ts` files which defined each component as a class. In TypeScript, a class name serves as both a value (the constructor) and a type (the instance), so patterns like `useRef<View>()` work. However, with function components, the name is only a value (the function signature) making `useRef<View>()` resolve to a ref holding the function itself, not the native element. **The fix**: Add companion type aliases alongside each component's value export. TypeScript's declaration merging makes the component name simultaneously a value (for JSX) and a type (for refs). ```diff // Before — required workaround - const ref = useRef<View>(null); + const ref = useRef<React.ComponentRef<typeof View>>(null); ref.current?.measure(...); - const inputRef = useRef<TextInput>(null); + const inputRef = useRef<React.ComponentRef<typeof TextInput>>(null); inputRef.current?.focus(); // After — original patterns work as-is const ref = useRef<View>(null); // View as a type = HostInstance ref.current?.measure(...); // HostInstance has measure() const inputRef = useRef<TextInput>(null); // TextInput as a type = TextInputInstance inputRef.current?.focus(); // TextInputInstance has focus() ``` Changelog: [General][Fixed] - Add companion instance type aliases for built-in components, preserving `useRef<Component>` patterns under the Strict TypeScript API Differential Revision: D103601923
1 parent 52d87f9 commit 6cb3c3d

1 file changed

Lines changed: 26 additions & 0 deletions

File tree

packages/react-native/index.js.flow

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,24 @@
2222

2323
// #region Components
2424

25+
// _HostInstance is mapped to a companion type export for each built-in component,
26+
// enabling ref type compatibility in TypeScript (e.g. `useRef<View>()`).
27+
import type {HostInstance as _HostInstance} from './src/private/types/HostInstance';
28+
2529
export type {ActivityIndicatorProps} from './Libraries/Components/ActivityIndicator/ActivityIndicator';
2630
export {default as ActivityIndicator} from './Libraries/Components/ActivityIndicator/ActivityIndicator';
31+
export type ActivityIndicator = _HostInstance;
2732

2833
export type {ButtonProps} from './Libraries/Components/Button';
2934
export {default as Button} from './Libraries/Components/Button';
35+
export type Button = _HostInstance;
3036

3137
export type {
3238
DrawerLayoutAndroidProps,
3339
DrawerSlideEvent,
3440
} from './Libraries/Components/DrawerAndroid/DrawerLayoutAndroid';
3541
export {default as DrawerLayoutAndroid} from './Libraries/Components/DrawerAndroid/DrawerLayoutAndroid';
42+
export type DrawerLayoutAndroid = _HostInstance;
3643

3744
export type {FlatListProps} from './Libraries/Lists/FlatList';
3845
export {default as FlatList} from './Libraries/Lists/FlatList';
@@ -56,13 +63,17 @@ export type {
5663
ImageURISource,
5764
} from './Libraries/Image/ImageSource';
5865
export {default as Image} from './Libraries/Image/Image';
66+
export type Image = _HostInstance;
5967
export {default as ImageBackground} from './Libraries/Image/ImageBackground';
68+
export type ImageBackground = _HostInstance;
6069

6170
export type {InputAccessoryViewProps} from './Libraries/Components/TextInput/InputAccessoryView';
6271
export {default as InputAccessoryView} from './Libraries/Components/TextInput/InputAccessoryView';
72+
export type InputAccessoryView = _HostInstance;
6373

6474
export type {KeyboardAvoidingViewProps} from './Libraries/Components/Keyboard/KeyboardAvoidingView';
6575
export {default as KeyboardAvoidingView} from './Libraries/Components/Keyboard/KeyboardAvoidingView';
76+
export type KeyboardAvoidingView = _HostInstance;
6677

6778
export type {LayoutConformanceProps} from './Libraries/Components/LayoutConformance/LayoutConformance';
6879
export {default as experimental_LayoutConformance} from './Libraries/Components/LayoutConformance/LayoutConformance';
@@ -74,27 +85,33 @@ export type {
7485
ModalPropsIOS,
7586
} from './Libraries/Modal/Modal';
7687
export {default as Modal} from './Libraries/Modal/Modal';
88+
export type Modal = _HostInstance;
7789

7890
export type {
7991
PressableAndroidRippleConfig,
8092
PressableProps,
8193
PressableStateCallbackType,
8294
} from './Libraries/Components/Pressable/Pressable';
8395
export {default as Pressable} from './Libraries/Components/Pressable/Pressable';
96+
export type Pressable = _HostInstance;
8497

8598
export type {ProgressBarAndroidProps} from './Libraries/Components/ProgressBarAndroid/ProgressBarAndroid';
8699
export {default as ProgressBarAndroid} from './Libraries/Components/ProgressBarAndroid/ProgressBarAndroid';
100+
export type ProgressBarAndroid = _HostInstance;
87101

88102
export type {
89103
RefreshControlProps,
90104
RefreshControlPropsAndroid,
91105
RefreshControlPropsIOS,
92106
} from './Libraries/Components/RefreshControl/RefreshControl';
93107
export {default as RefreshControl} from './Libraries/Components/RefreshControl/RefreshControl';
108+
export type RefreshControl = _HostInstance;
94109

95110
export {default as SafeAreaView} from './Libraries/Components/SafeAreaView/SafeAreaView';
111+
export type SafeAreaView = _HostInstance;
96112

97113
export type {
114+
PublicScrollViewInstance as ScrollView,
98115
ScrollViewImperativeMethods,
99116
ScrollViewScrollToOptions,
100117
ScrollResponderType,
@@ -118,15 +135,18 @@ export type {
118135
StatusBarStyle,
119136
} from './Libraries/Components/StatusBar/StatusBar';
120137
export {default as StatusBar} from './Libraries/Components/StatusBar/StatusBar';
138+
export type StatusBar = _HostInstance;
121139

122140
export type {
123141
SwitchChangeEvent,
124142
SwitchProps,
125143
} from './Libraries/Components/Switch/Switch';
126144
export {default as Switch} from './Libraries/Components/Switch/Switch';
145+
export type Switch = _HostInstance;
127146

128147
export type {TextProps} from './Libraries/Text/Text';
129148
export {default as Text} from './Libraries/Text/Text';
149+
export type Text = _HostInstance;
130150
export type {NativeTextProps as unstable_NativeTextProps} from './Libraries/Text/TextNativeComponent';
131151
export {NativeText as unstable_NativeText} from './Libraries/Text/TextNativeComponent';
132152
export {default as unstable_TextAncestorContext} from './Libraries/Text/TextAncestorContext';
@@ -151,20 +171,25 @@ export type {
151171
SubmitBehavior,
152172
} from './Libraries/Components/TextInput/TextInput';
153173
export {default as TextInput} from './Libraries/Components/TextInput/TextInput';
174+
export type {TextInputInstance as TextInput} from './Libraries/Components/TextInput/TextInput.flow';
154175

155176
export {default as Touchable} from './Libraries/Components/Touchable/Touchable';
156177

157178
export type {TouchableHighlightProps} from './Libraries/Components/Touchable/TouchableHighlight';
158179
export {default as TouchableHighlight} from './Libraries/Components/Touchable/TouchableHighlight';
180+
export type TouchableHighlight = _HostInstance;
159181

160182
export type {TouchableNativeFeedbackProps} from './Libraries/Components/Touchable/TouchableNativeFeedback';
161183
export {default as TouchableNativeFeedback} from './Libraries/Components/Touchable/TouchableNativeFeedback';
184+
export type TouchableNativeFeedback = _HostInstance;
162185

163186
export type {TouchableOpacityProps} from './Libraries/Components/Touchable/TouchableOpacity';
164187
export {default as TouchableOpacity} from './Libraries/Components/Touchable/TouchableOpacity';
188+
export type TouchableOpacity = _HostInstance;
165189

166190
export type {TouchableWithoutFeedbackProps} from './Libraries/Components/Touchable/TouchableWithoutFeedback';
167191
export {default as TouchableWithoutFeedback} from './Libraries/Components/Touchable/TouchableWithoutFeedback';
192+
export type TouchableWithoutFeedback = _HostInstance;
168193

169194
export type {
170195
AccessibilityActionEvent,
@@ -182,6 +207,7 @@ export type {
182207
ViewPropsIOS,
183208
} from './Libraries/Components/View/ViewPropTypes';
184209
export {default as View} from './Libraries/Components/View/View';
210+
export type View = _HostInstance;
185211
export {default as unstable_NativeView} from './Libraries/Components/View/ViewNativeComponent';
186212

187213
export type {

0 commit comments

Comments
 (0)