Skip to content

Commit 130a951

Browse files
rubennortefacebook-github-bot
authored andcommitted
Implement compatibility methods in ReactNativeElement and use it behind a flag
Summary: This implements all compatibility methods from `ReactFabricHostComponent` into `ReactNativeElement` and uses the feature flag `enableAccessToHostTreeInFabric` to choose between them at runtime. This flag is disabled everywhere so this won't have an effect for now. Both these classes implement the NativeMethods interface, so they're interchangeable as refs in existing product code. Changelog: [internal] bypass-github-export-checks Reviewed By: yungsters Differential Revision: D44304435 fbshipit-source-id: 43b5525b943e385287d7b00107147884b859c1fd
1 parent fbb3a9e commit 130a951

File tree

7 files changed

+313
-175
lines changed

7 files changed

+313
-175
lines changed

packages/react-native/Libraries/DOM/Nodes/ReactNativeElement.js

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,55 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8-
* @flow strict
8+
* @flow strict-local
99
*/
1010

1111
// flowlint unsafe-getters-setters:off
1212

1313
import type {
1414
HostComponent,
15+
INativeMethods,
16+
InternalInstanceHandle,
1517
MeasureInWindowOnSuccessCallback,
1618
MeasureLayoutOnSuccessCallback,
1719
MeasureOnSuccessCallback,
20+
ViewConfig,
1821
} from '../../Renderer/shims/ReactNativeTypes';
1922
import type {ElementRef} from 'react';
2023

24+
import TextInputState from '../../Components/TextInput/TextInputState';
25+
import {getFabricUIManager} from '../../ReactNative/FabricUIManager';
26+
import {create as createAttributePayload} from '../../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload';
27+
import warnForStyleProps from '../../ReactNative/ReactFabricPublicInstance/warnForStyleProps';
2128
import ReadOnlyElement from './ReadOnlyElement';
29+
import ReadOnlyNode from './ReadOnlyNode';
30+
import {getShadowNode} from './ReadOnlyNode';
31+
import nullthrows from 'nullthrows';
32+
33+
const noop = () => {};
34+
35+
export default class ReactNativeElement
36+
extends ReadOnlyElement
37+
implements INativeMethods
38+
{
39+
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
40+
__nativeTag: number;
41+
__internalInstanceHandle: InternalInstanceHandle;
42+
43+
_viewConfig: ViewConfig;
44+
45+
constructor(
46+
tag: number,
47+
viewConfig: ViewConfig,
48+
internalInstanceHandle: InternalInstanceHandle,
49+
) {
50+
super(internalInstanceHandle);
51+
52+
this.__nativeTag = tag;
53+
this.__internalInstanceHandle = internalInstanceHandle;
54+
this._viewConfig = viewConfig;
55+
}
2256

23-
export default class ReactNativeElement extends ReadOnlyElement {
2457
get offsetHeight(): number {
2558
throw new TypeError('Unimplemented');
2659
}
@@ -46,30 +79,71 @@ export default class ReactNativeElement extends ReadOnlyElement {
4679
*/
4780

4881
blur(): void {
49-
throw new TypeError('Unimplemented');
82+
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
83+
TextInputState.blurTextInput(this);
5084
}
5185

52-
focus(): void {
53-
throw new TypeError('Unimplemented');
86+
focus() {
87+
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
88+
TextInputState.focusTextInput(this);
5489
}
5590

56-
measure(callback: MeasureOnSuccessCallback): void {
57-
throw new TypeError('Unimplemented');
91+
measure(callback: MeasureOnSuccessCallback) {
92+
const node = getShadowNode(this);
93+
if (node != null) {
94+
nullthrows(getFabricUIManager()).measure(node, callback);
95+
}
5896
}
5997

60-
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {
61-
throw new TypeError('Unimplemented');
98+
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
99+
const node = getShadowNode(this);
100+
if (node != null) {
101+
nullthrows(getFabricUIManager()).measureInWindow(node, callback);
102+
}
62103
}
63104

64105
measureLayout(
65106
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
66107
onSuccess: MeasureLayoutOnSuccessCallback,
67108
onFail?: () => void /* currently unused */,
68-
): void {
69-
throw new TypeError('Unimplemented');
109+
) {
110+
if (!(relativeToNativeNode instanceof ReadOnlyNode)) {
111+
if (__DEV__) {
112+
console.error(
113+
'Warning: ref.measureLayout must be called with a ref to a native component.',
114+
);
115+
}
116+
117+
return;
118+
}
119+
120+
const toStateNode = getShadowNode(this);
121+
const fromStateNode = getShadowNode(relativeToNativeNode);
122+
123+
if (toStateNode != null && fromStateNode != null) {
124+
nullthrows(getFabricUIManager()).measureLayout(
125+
toStateNode,
126+
fromStateNode,
127+
onFail != null ? onFail : noop,
128+
onSuccess != null ? onSuccess : noop,
129+
);
130+
}
70131
}
71132

72133
setNativeProps(nativeProps: {...}): void {
73-
throw new TypeError('Unimplemented');
134+
if (__DEV__) {
135+
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
136+
}
137+
138+
const updatePayload = createAttributePayload(
139+
nativeProps,
140+
this._viewConfig.validAttributes,
141+
);
142+
143+
const node = getShadowNode(this);
144+
145+
if (node != null && updatePayload != null) {
146+
nullthrows(getFabricUIManager()).setNativeProps(node, updatePayload);
147+
}
74148
}
75149
}

packages/react-native/Libraries/DOM/Nodes/ReadOnlyElement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8-
* @flow strict
8+
* @flow strict-local
99
*/
1010

1111
// flowlint unsafe-getters-setters:off

packages/react-native/Libraries/DOM/Nodes/ReadOnlyNode.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8-
* @flow strict
8+
* @flow strict-local
99
*/
1010

1111
// flowlint unsafe-getters-setters:off
1212

13+
import type {
14+
InternalInstanceHandle,
15+
Node as ShadowNode,
16+
} from '../../Renderer/shims/ReactNativeTypes';
1317
import type NodeList from '../OldStyleCollections/NodeList';
1418
import type ReadOnlyElement from './ReadOnlyElement';
1519

20+
import ReactFabric from '../../Renderer/shims/ReactFabric';
21+
1622
export default class ReadOnlyNode {
23+
constructor(internalInstanceHandle: InternalInstanceHandle) {
24+
setInstanceHandle(this, internalInstanceHandle);
25+
}
26+
1727
get childNodes(): NodeList<ReadOnlyNode> {
1828
throw new TypeError('Unimplemented');
1929
}
@@ -165,3 +175,22 @@ export default class ReadOnlyNode {
165175
*/
166176
static DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number = 32;
167177
}
178+
179+
const INSTANCE_HANDLE_KEY = Symbol('internalInstanceHandle');
180+
181+
function getInstanceHandle(node: ReadOnlyNode): InternalInstanceHandle {
182+
// $FlowExpectedError[prop-missing]
183+
return node[INSTANCE_HANDLE_KEY];
184+
}
185+
186+
function setInstanceHandle(
187+
node: ReadOnlyNode,
188+
instanceHandle: InternalInstanceHandle,
189+
): void {
190+
// $FlowExpectedError[prop-missing]
191+
node[INSTANCE_HANDLE_KEY] = instanceHandle;
192+
}
193+
194+
export function getShadowNode(node: ReadOnlyNode): ?ShadowNode {
195+
return ReactFabric.getNodeFromInternalInstanceHandle(getInstanceHandle(node));
196+
}

packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
*/
1010

1111
import type {
12-
AttributeConfiguration,
1312
HostComponent,
1413
INativeMethods,
1514
InternalInstanceHandle,
@@ -24,6 +23,7 @@ import TextInputState from '../../Components/TextInput/TextInputState';
2423
import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric';
2524
import {getFabricUIManager} from '../FabricUIManager';
2625
import {create} from './ReactNativeAttributePayload';
26+
import warnForStyleProps from './warnForStyleProps';
2727
import nullthrows from 'nullthrows';
2828

2929
const {
@@ -149,24 +149,3 @@ export default class ReactFabricHostComponent implements INativeMethods {
149149
}
150150
}
151151
}
152-
153-
function warnForStyleProps(
154-
props: {...},
155-
validAttributes: AttributeConfiguration,
156-
): void {
157-
if (__DEV__) {
158-
for (const key in validAttributes.style) {
159-
if (!(validAttributes[key] || props[key] === undefined)) {
160-
console.error(
161-
'You are setting the style `{ %s' +
162-
': ... }` as a prop. You ' +
163-
'should nest it in a style object. ' +
164-
'E.g. `{ style: { %s' +
165-
': ... } }`',
166-
key,
167-
key,
168-
);
169-
}
170-
}
171-
}
172-
}

packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,42 @@
88
* @flow strict-local
99
*/
1010

11+
import type ReactNativeElement from '../../DOM/Nodes/ReactNativeElement';
1112
import typeof ReactFabricType from '../../Renderer/shims/ReactFabric';
1213
import type {
1314
InternalInstanceHandle,
15+
Node,
1416
ViewConfig,
1517
} from '../../Renderer/shims/ReactNativeTypes';
16-
import type ReactFabricHostComponentType from './ReactFabricHostComponent';
18+
import type ReactFabricHostComponent from './ReactFabricHostComponent';
19+
20+
import ReactNativeFeatureFlags from '../ReactNativeFeatureFlags';
1721

1822
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
19-
let ReactFabricHostComponent: Class<ReactFabricHostComponentType>;
23+
let PublicInstanceClass:
24+
| Class<ReactFabricHostComponent>
25+
| Class<ReactNativeElement>;
26+
2027
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
2128
let ReactFabric: ReactFabricType;
2229

2330
export function createPublicInstance(
2431
tag: number,
2532
viewConfig: ViewConfig,
2633
internalInstanceHandle: InternalInstanceHandle,
27-
): ReactFabricHostComponentType {
28-
if (ReactFabricHostComponent == null) {
29-
ReactFabricHostComponent = require('./ReactFabricHostComponent').default;
34+
): ReactFabricHostComponent | ReactNativeElement {
35+
if (PublicInstanceClass == null) {
36+
// We don't use inline requires in react-native, so this forces lazy loading
37+
// the right module to avoid eagerly loading both.
38+
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
39+
PublicInstanceClass =
40+
require('../../DOM/Nodes/ReactNativeElement').default;
41+
} else {
42+
PublicInstanceClass = require('./ReactFabricHostComponent').default;
43+
}
3044
}
31-
return new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
45+
46+
return new PublicInstanceClass(tag, viewConfig, internalInstanceHandle);
3247
}
3348

3449
export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
@@ -39,14 +54,14 @@ export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
3954
}
4055

4156
export function getNativeTagFromPublicInstance(
42-
publicInstance: ReactFabricHostComponentType,
57+
publicInstance: ReactFabricHostComponent | ReactNativeElement,
4358
): number {
4459
return publicInstance.__nativeTag;
4560
}
4661

4762
export function getNodeFromPublicInstance(
48-
publicInstance: ReactFabricHostComponentType,
49-
): mixed {
63+
publicInstance: ReactFabricHostComponent | ReactNativeElement,
64+
): ?Node {
5065
// Avoid loading ReactFabric if using an instance from the legacy renderer.
5166
if (publicInstance.__internalInstanceHandle == null) {
5267
return null;
@@ -55,7 +70,6 @@ export function getNodeFromPublicInstance(
5570
if (ReactFabric == null) {
5671
ReactFabric = require('../../Renderer/shims/ReactFabric');
5772
}
58-
5973
return ReactFabric.getNodeFromInternalInstanceHandle(
6074
publicInstance.__internalInstanceHandle,
6175
);

0 commit comments

Comments
 (0)