Skip to content

Commit

Permalink
Extracted definition and access to public instances to a separate mod…
Browse files Browse the repository at this point in the history
…ule in Fabric (v2)
  • Loading branch information
rubennorte committed Mar 16, 2023
1 parent e8bdba3 commit 90087f0
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 206 deletions.
5 changes: 5 additions & 0 deletions packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
findNodeHandle,
dispatchCommand,
sendAccessibilityEvent,
getNodeFromInternalInstanceHandle,
} from './ReactNativePublicCompat';

// $FlowFixMe[missing-local-annot]
Expand Down Expand Up @@ -119,6 +120,10 @@ export {
// This export is typically undefined in production builds.
// See the "enableGetInspectorDataForInstanceInProduction" flag.
getInspectorDataForInstance,
// The public instance has a reference to the internal instance handle.
// This method allows it to acess the most recent shadow node for
// the instance (it's only accessible through it).
getNodeFromInternalInstanceHandle,
};

injectIntoDevTools({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import {getPublicInstance} from './ReactFabricHostConfig';
// This is ok in DOM because they types are interchangeable, but in React Native
// they aren't.
function getInstanceFromNode(node: Instance | TextInstance): Fiber | null {
const instance: Instance = (node: $FlowFixMe); // In React Native, node is never a text instance

if (
instance.canonical != null &&
instance.canonical.internalInstanceHandle != null
) {
return instance.canonical.internalInstanceHandle;
}

// $FlowFixMe[incompatible-return] DevTools incorrectly passes a fiber in React Native.
return node;
}
Expand Down
188 changes: 29 additions & 159 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,13 @@
* @flow
*/

import type {ElementRef} from 'react';
import type {
HostComponent,
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
INativeMethods,
ViewConfig,
TouchedViewDataAtPoint,
} from './ReactNativeTypes';

import {warnForStyleProps} from './NativeMethodsMixinUtils';
import type {TouchedViewDataAtPoint, ViewConfig} from './ReactNativeTypes';
import {
createPublicInstance,
type ReactFabricHostComponent,
} from './ReactFabricPublicInstance';
import {create, diff} from './ReactNativeAttributePayload';

import {dispatchEvent} from './ReactFabricEventEmitter';

import {
DefaultEventPriority,
DiscreteEventPriority,
Expand All @@ -31,7 +22,6 @@ import {
// Modules provided by RN:
import {
ReactNativeViewConfigRegistry,
TextInputState,
deepFreezeAndThrowOnMutationInDev,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

Expand All @@ -46,14 +36,9 @@ const {
appendChildToSet: appendChildNodeToSet,
completeRoot,
registerEventHandler,
measure: fabricMeasure,
measureInWindow: fabricMeasureInWindow,
measureLayout: fabricMeasureLayout,
unstable_DefaultEventPriority: FabricDefaultPriority,
unstable_DiscreteEventPriority: FabricDiscretePriority,
unstable_getCurrentEventPriority: fabricGetCurrentEventPriority,
setNativeProps,
getBoundingClientRect: fabricGetBoundingClientRect,
} = nativeFabricUIManager;

const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
Expand All @@ -68,9 +53,17 @@ type Node = Object;
export type Type = string;
export type Props = Object;
export type Instance = {
// Reference to the shadow node.
node: Node,
canonical: ReactFabricHostComponent,
...
canonical: {
nativeTag: number,
viewConfig: ViewConfig,
currentProps: Props,
// Reference to the React handle (the fiber)
internalInstanceHandle: Object,
// Exposed through refs.
publicInstance: ReactFabricHostComponent,
},
};
export type TextInstance = {node: Node, ...};
export type HydratableInstance = Instance | TextInstance;
Expand Down Expand Up @@ -104,137 +97,6 @@ if (registerEventHandler) {
registerEventHandler(dispatchEvent);
}

const noop = () => {};

/**
* This is used for refs on host components.
*/
class ReactFabricHostComponent implements INativeMethods {
_nativeTag: number;
viewConfig: ViewConfig;
currentProps: Props;
_internalInstanceHandle: Object;

constructor(
tag: number,
viewConfig: ViewConfig,
props: Props,
internalInstanceHandle: Object,
) {
this._nativeTag = tag;
this.viewConfig = viewConfig;
this.currentProps = props;
this._internalInstanceHandle = internalInstanceHandle;
}

blur() {
TextInputState.blurTextInput(this);
}

focus() {
TextInputState.focusTextInput(this);
}

measure(callback: MeasureOnSuccessCallback) {
const node = getShadowNodeFromInternalInstanceHandle(
this._internalInstanceHandle,
);
if (node != null) {
fabricMeasure(node, callback);
}
}

measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
const node = getShadowNodeFromInternalInstanceHandle(
this._internalInstanceHandle,
);
if (node != null) {
fabricMeasureInWindow(node, callback);
}
}

measureLayout(
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void /* currently unused */,
) {
if (
typeof relativeToNativeNode === 'number' ||
!(relativeToNativeNode instanceof ReactFabricHostComponent)
) {
if (__DEV__) {
console.error(
'Warning: ref.measureLayout must be called with a ref to a native component.',
);
}

return;
}

const toStateNode = getShadowNodeFromInternalInstanceHandle(
this._internalInstanceHandle,
);
const fromStateNode = getShadowNodeFromInternalInstanceHandle(
relativeToNativeNode._internalInstanceHandle,
);

if (toStateNode != null && fromStateNode != null) {
fabricMeasureLayout(
toStateNode,
fromStateNode,
onFail != null ? onFail : noop,
onSuccess != null ? onSuccess : noop,
);
}
}

unstable_getBoundingClientRect(): DOMRect {
const node = getShadowNodeFromInternalInstanceHandle(
this._internalInstanceHandle,
);
if (node != null) {
const rect = fabricGetBoundingClientRect(node);

if (rect) {
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
}
}

// Empty rect if any of the above failed
return new DOMRect(0, 0, 0, 0);
}

setNativeProps(nativeProps: Object) {
if (__DEV__) {
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
}
const updatePayload = create(nativeProps, this.viewConfig.validAttributes);

const node = getShadowNodeFromInternalInstanceHandle(
this._internalInstanceHandle,
);
if (node != null && updatePayload != null) {
setNativeProps(node, updatePayload);
}
}
}

type ParamOf<Fn> = $Call<<T>((arg: T) => mixed) => T, Fn>;
type ShadowNode = ParamOf<(typeof nativeFabricUIManager)['measure']>;

export function getShadowNodeFromInternalInstanceHandle(
internalInstanceHandle: mixed,
): ?ShadowNode {
return (
// $FlowExpectedError[incompatible-return] internalInstanceHandle is opaque but we need to make an exception here.
internalInstanceHandle &&
// $FlowExpectedError[incompatible-return]
internalInstanceHandle.stateNode &&
// $FlowExpectedError[incompatible-use]
internalInstanceHandle.stateNode.node
);
}

export * from 'react-reconciler/src/ReactFiberHostConfigWithNoMutation';
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoHydration';
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoScopes';
Expand Down Expand Up @@ -280,16 +142,21 @@ export function createInstance(
internalInstanceHandle, // internalInstanceHandle
);

const component = new ReactFabricHostComponent(
const component = createPublicInstance(
tag,
viewConfig,
props,
internalInstanceHandle,
);

return {
node: node,
canonical: component,
canonical: {
nativeTag: tag,
viewConfig,
currentProps: props,
internalInstanceHandle,
publicInstance: component,
},
};
}

Expand Down Expand Up @@ -359,12 +226,15 @@ export function getChildHostContext(
}

export function getPublicInstance(instance: Instance): null | PublicInstance {
if (instance.canonical) {
return instance.canonical;
if (instance.canonical != null && instance.canonical.publicInstance != null) {
return instance.canonical.publicInstance;
}

// For compatibility with Paper
// For compatibility with the legacy renderer, in case it's used with Fabric
// in the same app.
// $FlowExpectedError[prop-missing]
if (instance._nativeTag != null) {
// $FlowExpectedError[incompatible-return]
return instance;
}

Expand Down
Loading

0 comments on commit 90087f0

Please sign in to comment.