main
js 216 lines 6.6 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {Node} from './ReactNativeTypes';
11 import type {ElementRef, ElementType} from 'react';
12 import type {PublicInstance} from 'react-native/react-private-interface';
13
14 // Modules provided by RN:
15 import {
16 getNodeFromPublicInstance,
17 getNativeTagFromPublicInstance,
18 getInternalInstanceHandleFromPublicInstance,
19 } from 'react-native/react-private-interface';
20
21 import {
22 findHostInstance,
23 findHostInstanceWithWarning,
24 } from 'react-reconciler/src/ReactFiberReconciler';
25 import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
26 import getComponentNameFromType from 'shared/getComponentNameFromType';
27 import {
28 current as currentOwner,
29 isRendering,
30 } from 'react-reconciler/src/ReactCurrentFiber';
31
32 export function findHostInstance_DEPRECATED<TElementType: ElementType>(
33 componentOrHandle: ?(ElementRef<TElementType> | number),
34 ): ?PublicInstance {
35 if (__DEV__) {
36 const owner = currentOwner;
37 if (owner !== null && isRendering && owner.stateNode !== null) {
38 if (!owner.stateNode._warnedAboutRefsInRender) {
39 console.error(
40 '%s is accessing findNodeHandle inside its render(). ' +
41 'render() should be a pure function of props and state. It should ' +
42 'never access something that requires stale data from the previous ' +
43 'render, such as refs. Move this logic to componentDidMount and ' +
44 'componentDidUpdate instead.',
45 getComponentNameFromType(owner.type) || 'A component',
46 );
47 }
48
49 owner.stateNode._warnedAboutRefsInRender = true;
50 }
51 }
52
53 if (componentOrHandle == null) {
54 return null;
55 }
56
57 // For compatibility with Fabric instances
58 if (
59 componentOrHandle.canonical &&
60 componentOrHandle.canonical.publicInstance
61 ) {
62 // $FlowExpectedError[incompatible-type] Can't refine componentOrHandle as a Fabric instance
63 return componentOrHandle.canonical.publicInstance;
64 }
65
66 let hostInstance;
67 if (__DEV__) {
68 hostInstance = findHostInstanceWithWarning(
69 componentOrHandle,
70 'findHostInstance_DEPRECATED',
71 );
72 } else {
73 hostInstance = findHostInstance(componentOrHandle);
74 }
75
76 // findHostInstance handles legacy vs. Fabric differences correctly
77 // $FlowFixMe[incompatible-exact] we need to fix the definition of `HostComponent` to use NativeMethods as an interface, not as a type.
78 // $FlowFixMe[incompatible-type]
79 return hostInstance;
80 }
81
82 export function findNodeHandle(componentOrHandle: any): ?number {
83 if (__DEV__) {
84 const owner = currentOwner;
85 if (owner !== null && isRendering && owner.stateNode !== null) {
86 if (!owner.stateNode._warnedAboutRefsInRender) {
87 console.error(
88 '%s is accessing findNodeHandle inside its render(). ' +
89 'render() should be a pure function of props and state. It should ' +
90 'never access something that requires stale data from the previous ' +
91 'render, such as refs. Move this logic to componentDidMount and ' +
92 'componentDidUpdate instead.',
93 getComponentNameFromType(owner.type) || 'A component',
94 );
95 }
96
97 owner.stateNode._warnedAboutRefsInRender = true;
98 }
99 }
100
101 if (componentOrHandle == null) {
102 return null;
103 }
104
105 if (typeof componentOrHandle === 'number') {
106 // Already a node handle
107 return componentOrHandle;
108 }
109
110 // For compatibility with Fabric instances
111 if (
112 componentOrHandle.canonical != null &&
113 componentOrHandle.canonical.nativeTag != null
114 ) {
115 return componentOrHandle.canonical.nativeTag;
116 }
117
118 // For compatibility with Fabric public instances
119 const nativeTag = getNativeTagFromPublicInstance(componentOrHandle);
120 if (nativeTag) {
121 return nativeTag;
122 }
123
124 let hostInstance;
125 if (__DEV__) {
126 hostInstance = findHostInstanceWithWarning(
127 componentOrHandle,
128 'findNodeHandle',
129 );
130 } else {
131 hostInstance = findHostInstance(componentOrHandle);
132 }
133
134 if (hostInstance == null) {
135 // $FlowFixMe[incompatible-type] Flow limitation in refining an opaque type
136 return hostInstance;
137 }
138
139 // $FlowFixMe[incompatible-type] Necessary when running Flow on the legacy renderer
140 return getNativeTagFromPublicInstance(hostInstance);
141 }
142
143 export function dispatchCommand(
144 handle: any,
145 command: string,
146 args: Array<any>,
147 ) {
148 const node = getNodeFromPublicInstance(handle);
149
150 if (node != null) {
151 nativeFabricUIManager.dispatchCommand(node, command, args);
152 } else {
153 if (__DEV__) {
154 console.error(
155 "dispatchCommand was called with a ref that isn't a " +
156 'native component. Use React.forwardRef to get access to the underlying native component',
157 );
158 }
159 }
160 }
161
162 export function sendAccessibilityEvent(handle: any, eventType: string) {
163 const node = getNodeFromPublicInstance(handle);
164
165 if (node != null) {
166 nativeFabricUIManager.sendAccessibilityEvent(node, eventType);
167 } else {
168 if (__DEV__) {
169 console.error(
170 "sendAccessibilityEvent was called with a ref that isn't a " +
171 'native component. Use React.forwardRef to get access to the underlying native component',
172 );
173 }
174 }
175 }
176
177 export function getNodeFromInternalInstanceHandle(
178 internalInstanceHandle: mixed,
179 ): ?Node {
180 return (
181 // $FlowExpectedError[incompatible-type] internalInstanceHandle is opaque but we need to make an exception here.
182 internalInstanceHandle &&
183 // $FlowExpectedError[incompatible-type]
184 internalInstanceHandle.stateNode &&
185 // $FlowExpectedError[incompatible-use]
186 internalInstanceHandle.stateNode.node
187 );
188 }
189
190 export function isChildPublicInstance(
191 parentInstance: PublicInstance,
192 childInstance: PublicInstance,
193 ): boolean {
194 if (__DEV__) {
195 const parentInternalInstanceHandle =
196 // $FlowExpectedError[incompatible-call] Type for parentInstance should have been PublicInstance from ReactFiberConfigFabric.
197 getInternalInstanceHandleFromPublicInstance(parentInstance);
198 const childInternalInstanceHandle =
199 // $FlowExpectedError[incompatible-call] Type for childInstance should have been PublicInstance from ReactFiberConfigFabric.
200 getInternalInstanceHandleFromPublicInstance(childInstance);
201
202 if (
203 parentInternalInstanceHandle != null &&
204 childInternalInstanceHandle != null
205 ) {
206 return doesFiberContain(
207 parentInternalInstanceHandle,
208 childInternalInstanceHandle,
209 );
210 }
211
212 return false;
213 } else {
214 throw new Error('isChildPublicInstance() is not available in production.');
215 }
216 }