Skip to content

Commit c67a519

Browse files
committed
Extracted definition and access to public instances to a separate module in Fabric
1 parent eb616a1 commit c67a519

14 files changed

+360
-222
lines changed

packages/react-native-renderer/src/NativeMethodsMixinUtils.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,6 @@ export function mountSafeCallback_NOT_REALLY_SAFE(
4545
};
4646
}
4747

48-
export function throwOnStylesProp(component: any, props: any) {
49-
if (props.styles !== undefined) {
50-
const owner = component._owner || null;
51-
const name = component.constructor.displayName;
52-
let msg =
53-
'`styles` is not a supported property of `' +
54-
name +
55-
'`, did ' +
56-
'you mean `style` (singular)?';
57-
if (owner && owner.constructor && owner.constructor.displayName) {
58-
msg +=
59-
'\n\nCheck the `' +
60-
owner.constructor.displayName +
61-
'` parent ' +
62-
' component.';
63-
}
64-
throw new Error(msg);
65-
}
66-
}
67-
6848
export function warnForStyleProps(props: any, validAttributes: any) {
6949
if (__DEV__) {
7050
for (const key in validAttributes.style) {

packages/react-native-renderer/src/ReactFabric.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export {
122122
};
123123

124124
injectIntoDevTools({
125+
// $FlowExpectedError[incompatible-call] The type of `Instance` in `getClosestInstanceFromNode` does not match in Fabric and the legacy renderer, so it fails to typecheck here.
125126
findFiberByHostInstance: getClosestInstanceFromNode,
126127
bundleType: __DEV__ ? 1 : 0,
127128
version: ReactVersion,

packages/react-native-renderer/src/ReactFabricComponentTree.js

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,53 @@
33
*
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
68
*/
79

8-
function getInstanceFromInstance(instanceHandle) {
9-
return instanceHandle;
10+
import type {
11+
PublicInstance,
12+
Instance,
13+
Props,
14+
TextInstance,
15+
} from './ReactFabricHostConfig';
16+
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
17+
import {getPublicInstance} from './ReactFabricHostConfig';
18+
19+
// `node` is typed incorrectly here. The proper type should be `PublicInstance`.
20+
// This is ok in DOM because they types are interchangeable, but in React Native
21+
// they aren't.
22+
function getInstanceFromNode(node: Instance | TextInstance): Fiber | null {
23+
const instance: Instance = (node: $FlowFixMe); // In React Native, node is never a text instance
24+
25+
if (
26+
instance.internals != null &&
27+
instance.internals.internalInstanceHandle != null
28+
) {
29+
return instance.internals.internalInstanceHandle;
30+
}
31+
32+
// $FlowFixMe[incompatible-return] DevTools incorrectly passes a fiber in React Native.
33+
return node;
1034
}
1135

12-
function getTagFromInstance(inst) {
13-
const nativeInstance = inst.stateNode.canonical;
36+
function getNodeFromInstance(fiber: Fiber): PublicInstance {
37+
const publicInstance = getPublicInstance(fiber.stateNode);
1438

15-
if (!nativeInstance._nativeTag) {
16-
throw new Error('All native instances should have a tag.');
39+
if (publicInstance == null) {
40+
throw new Error('Could not find host instance from fiber');
1741
}
1842

19-
return nativeInstance;
43+
return publicInstance;
44+
}
45+
46+
function getFiberCurrentPropsFromNode(instance: Instance): Props {
47+
return instance && instance.internals && instance.internals.currentProps;
2048
}
2149

2250
export {
23-
getInstanceFromInstance as getClosestInstanceFromNode,
24-
getInstanceFromInstance as getInstanceFromNode,
25-
getTagFromInstance as getNodeFromInstance,
51+
getInstanceFromNode,
52+
getInstanceFromNode as getClosestInstanceFromNode,
53+
getNodeFromInstance,
54+
getFiberCurrentPropsFromNode,
2655
};
27-
28-
export function getFiberCurrentPropsFromNode(inst) {
29-
return inst.canonical.currentProps;
30-
}

packages/react-native-renderer/src/ReactFabricEventEmitter.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import {batchedUpdates} from './legacy-events/ReactGenericBatching';
2626
import accumulateInto from './legacy-events/accumulateInto';
2727

28+
import {getPublicInstance} from './ReactFabricHostConfig';
2829
import getListener from './ReactNativeGetListener';
2930
import {runEventsInBatch} from './legacy-events/EventBatching';
3031

@@ -92,7 +93,8 @@ export function dispatchEvent(
9293
const stateNode = targetFiber.stateNode;
9394
// Guard against Fiber being unmounted
9495
if (stateNode != null) {
95-
eventTarget = stateNode.canonical;
96+
// $FlowExpectedError[incompatible-cast] public instances in Fabric do not implement `EventTarget` yet.
97+
eventTarget = (getPublicInstance(stateNode): EventTarget);
9698
}
9799
}
98100

packages/react-native-renderer/src/ReactFabricHostConfig.js

Lines changed: 36 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,13 @@
77
* @flow
88
*/
99

10-
import type {ElementRef} from 'react';
11-
import type {
12-
HostComponent,
13-
MeasureInWindowOnSuccessCallback,
14-
MeasureLayoutOnSuccessCallback,
15-
MeasureOnSuccessCallback,
16-
NativeMethods,
17-
ViewConfig,
18-
TouchedViewDataAtPoint,
19-
} from './ReactNativeTypes';
20-
10+
import type {TouchedViewDataAtPoint, ViewConfig} from './ReactNativeTypes';
2111
import {
22-
mountSafeCallback_NOT_REALLY_SAFE,
23-
warnForStyleProps,
24-
} from './NativeMethodsMixinUtils';
12+
createPublicInstance,
13+
type ReactFabricHostComponent,
14+
} from './ReactFabricPublicInstance';
2515
import {create, diff} from './ReactNativeAttributePayload';
26-
2716
import {dispatchEvent} from './ReactFabricEventEmitter';
28-
2917
import {
3018
DefaultEventPriority,
3119
DiscreteEventPriority,
@@ -34,7 +22,6 @@ import {
3422
// Modules provided by RN:
3523
import {
3624
ReactNativeViewConfigRegistry,
37-
TextInputState,
3825
deepFreezeAndThrowOnMutationInDev,
3926
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
4027

@@ -49,14 +36,9 @@ const {
4936
appendChildToSet: appendChildNodeToSet,
5037
completeRoot,
5138
registerEventHandler,
52-
measure: fabricMeasure,
53-
measureInWindow: fabricMeasureInWindow,
54-
measureLayout: fabricMeasureLayout,
5539
unstable_DefaultEventPriority: FabricDefaultPriority,
5640
unstable_DiscreteEventPriority: FabricDiscretePriority,
5741
unstable_getCurrentEventPriority: fabricGetCurrentEventPriority,
58-
setNativeProps,
59-
getBoundingClientRect: fabricGetBoundingClientRect,
6042
} = nativeFabricUIManager;
6143

6244
const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
@@ -71,9 +53,18 @@ type Node = Object;
7153
export type Type = string;
7254
export type Props = Object;
7355
export type Instance = {
56+
// Reference to the shadow node.
7457
node: Node,
75-
canonical: ReactFabricHostComponent,
76-
...
58+
// Exposed through refs.
59+
publicInstance: ReactFabricHostComponent,
60+
// We define this as an object instead of as separate fields to simplify
61+
// making copies of instance where `internals` don't change.
62+
internals: {
63+
nativeTag: number,
64+
viewConfig: ViewConfig,
65+
currentProps: Props,
66+
internalInstanceHandle: Object,
67+
},
7768
};
7869
export type TextInstance = {node: Node, ...};
7970
export type HydratableInstance = Instance | TextInstance;
@@ -107,114 +98,6 @@ if (registerEventHandler) {
10798
registerEventHandler(dispatchEvent);
10899
}
109100

110-
/**
111-
* This is used for refs on host components.
112-
*/
113-
class ReactFabricHostComponent implements NativeMethods {
114-
_nativeTag: number;
115-
viewConfig: ViewConfig;
116-
currentProps: Props;
117-
_internalInstanceHandle: Object;
118-
119-
constructor(
120-
tag: number,
121-
viewConfig: ViewConfig,
122-
props: Props,
123-
internalInstanceHandle: Object,
124-
) {
125-
this._nativeTag = tag;
126-
this.viewConfig = viewConfig;
127-
this.currentProps = props;
128-
this._internalInstanceHandle = internalInstanceHandle;
129-
}
130-
131-
blur() {
132-
TextInputState.blurTextInput(this);
133-
}
134-
135-
focus() {
136-
TextInputState.focusTextInput(this);
137-
}
138-
139-
measure(callback: MeasureOnSuccessCallback) {
140-
const {stateNode} = this._internalInstanceHandle;
141-
if (stateNode != null) {
142-
fabricMeasure(
143-
stateNode.node,
144-
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
145-
);
146-
}
147-
}
148-
149-
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
150-
const {stateNode} = this._internalInstanceHandle;
151-
if (stateNode != null) {
152-
fabricMeasureInWindow(
153-
stateNode.node,
154-
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
155-
);
156-
}
157-
}
158-
159-
measureLayout(
160-
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
161-
onSuccess: MeasureLayoutOnSuccessCallback,
162-
onFail?: () => void /* currently unused */,
163-
) {
164-
if (
165-
typeof relativeToNativeNode === 'number' ||
166-
!(relativeToNativeNode instanceof ReactFabricHostComponent)
167-
) {
168-
if (__DEV__) {
169-
console.error(
170-
'Warning: ref.measureLayout must be called with a ref to a native component.',
171-
);
172-
}
173-
174-
return;
175-
}
176-
177-
const toStateNode = this._internalInstanceHandle.stateNode;
178-
const fromStateNode =
179-
relativeToNativeNode._internalInstanceHandle.stateNode;
180-
181-
if (toStateNode != null && fromStateNode != null) {
182-
fabricMeasureLayout(
183-
toStateNode.node,
184-
fromStateNode.node,
185-
mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
186-
mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
187-
);
188-
}
189-
}
190-
191-
unstable_getBoundingClientRect(): DOMRect {
192-
const {stateNode} = this._internalInstanceHandle;
193-
if (stateNode != null) {
194-
const rect = fabricGetBoundingClientRect(stateNode.node);
195-
196-
if (rect) {
197-
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
198-
}
199-
}
200-
201-
// Empty rect if any of the above failed
202-
return new DOMRect(0, 0, 0, 0);
203-
}
204-
205-
setNativeProps(nativeProps: Object) {
206-
if (__DEV__) {
207-
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
208-
}
209-
const updatePayload = create(nativeProps, this.viewConfig.validAttributes);
210-
211-
const {stateNode} = this._internalInstanceHandle;
212-
if (stateNode != null && updatePayload != null) {
213-
setNativeProps(stateNode.node, updatePayload);
214-
}
215-
}
216-
}
217-
218101
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoMutation';
219102
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoHydration';
220103
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoScopes';
@@ -260,16 +143,21 @@ export function createInstance(
260143
internalInstanceHandle, // internalInstanceHandle
261144
);
262145

263-
const component = new ReactFabricHostComponent(
146+
const component = createPublicInstance(
264147
tag,
265148
viewConfig,
266-
props,
267149
internalInstanceHandle,
268150
);
269151

270152
return {
271153
node: node,
272-
canonical: component,
154+
publicInstance: component,
155+
internals: {
156+
nativeTag: tag,
157+
viewConfig,
158+
currentProps: props,
159+
internalInstanceHandle,
160+
},
273161
};
274162
}
275163

@@ -339,12 +227,15 @@ export function getChildHostContext(
339227
}
340228

341229
export function getPublicInstance(instance: Instance): null | PublicInstance {
342-
if (instance.canonical) {
343-
return instance.canonical;
230+
if (instance.publicInstance != null) {
231+
return instance.publicInstance;
344232
}
345233

346-
// For compatibility with Paper
234+
// For compatibility with the legacy renderer, in case it's used with Fabric
235+
// in the same app.
236+
// $FlowExpectedError[prop-missing]
347237
if (instance._nativeTag != null) {
238+
// $FlowExpectedError[incompatible-return]
348239
return instance;
349240
}
350241

@@ -363,12 +254,12 @@ export function prepareUpdate(
363254
newProps: Props,
364255
hostContext: HostContext,
365256
): null | Object {
366-
const viewConfig = instance.canonical.viewConfig;
257+
const viewConfig = instance.internals.viewConfig;
367258
const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
368259
// TODO: If the event handlers have changed, we need to update the current props
369260
// in the commit phase but there is no host config hook to do it yet.
370261
// So instead we hack it by updating it in the render phase.
371-
instance.canonical.currentProps = newProps;
262+
instance.internals.currentProps = newProps;
372263
return updatePayload;
373264
}
374265

@@ -447,7 +338,8 @@ export function cloneInstance(
447338
}
448339
return {
449340
node: clone,
450-
canonical: instance.canonical,
341+
publicInstance: instance.publicInstance,
342+
internals: instance.internals,
451343
};
452344
}
453345

@@ -457,15 +349,16 @@ export function cloneHiddenInstance(
457349
props: Props,
458350
internalInstanceHandle: Object,
459351
): Instance {
460-
const viewConfig = instance.canonical.viewConfig;
352+
const viewConfig = instance.internals.viewConfig;
461353
const node = instance.node;
462354
const updatePayload = create(
463355
{style: {display: 'none'}},
464356
viewConfig.validAttributes,
465357
);
466358
return {
467359
node: cloneNodeWithNewProps(node, updatePayload),
468-
canonical: instance.canonical,
360+
publicInstance: instance.publicInstance,
361+
internals: instance.internals,
469362
};
470363
}
471364

0 commit comments

Comments
 (0)