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
  • Loading branch information
rubennorte committed Mar 6, 2023
1 parent eb616a1 commit 5a7d440
Show file tree
Hide file tree
Showing 14 changed files with 360 additions and 222 deletions.
20 changes: 0 additions & 20 deletions packages/react-native-renderer/src/NativeMethodsMixinUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,6 @@ export function mountSafeCallback_NOT_REALLY_SAFE(
};
}

export function throwOnStylesProp(component: any, props: any) {
if (props.styles !== undefined) {
const owner = component._owner || null;
const name = component.constructor.displayName;
let msg =
'`styles` is not a supported property of `' +
name +
'`, did ' +
'you mean `style` (singular)?';
if (owner && owner.constructor && owner.constructor.displayName) {
msg +=
'\n\nCheck the `' +
owner.constructor.displayName +
'` parent ' +
' component.';
}
throw new Error(msg);
}
}

export function warnForStyleProps(props: any, validAttributes: any) {
if (__DEV__) {
for (const key in validAttributes.style) {
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export {
};

injectIntoDevTools({
// $FlowExpectedError[incompatible-call] The type of `Instance` in `getClosestInstanceFromNode` does not match in Fabric and Paper, so it fails to typecheck here.
findFiberByHostInstance: getClosestInstanceFromNode,
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
Expand Down
53 changes: 39 additions & 14 deletions packages/react-native-renderer/src/ReactFabricComponentTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,53 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
*/

function getInstanceFromInstance(instanceHandle) {
return instanceHandle;
import type {
PublicInstance,
Instance,
Props,
TextInstance,
} from './ReactFabricHostConfig';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {getPublicInstance} from './ReactFabricHostConfig';

// `node` is typed incorrectly here. The proper type should be `PublicInstance`.
// 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.internals != null &&
instance.internals.internalInstanceHandle != null
) {
return instance.internals.internalInstanceHandle;
}

// $FlowFixMe[incompatible-return] DevTools incorrectly passes a fiber in React Native.
return node;
}

function getTagFromInstance(inst) {
const nativeInstance = inst.stateNode.canonical;
function getNodeFromInstance(fiber: Fiber): PublicInstance {
const publicInstance = getPublicInstance(fiber.stateNode);

if (!nativeInstance._nativeTag) {
throw new Error('All native instances should have a tag.');
if (publicInstance == null) {
throw new Error('Could not find host instance from fiber');
}

return nativeInstance;
return publicInstance;
}

function getFiberCurrentPropsFromNode(instance: Instance): Props {
return instance && instance.internals && instance.internals.currentProps;
}

export {
getInstanceFromInstance as getClosestInstanceFromNode,
getInstanceFromInstance as getInstanceFromNode,
getTagFromInstance as getNodeFromInstance,
getInstanceFromNode,
getInstanceFromNode as getClosestInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
};

export function getFiberCurrentPropsFromNode(inst) {
return inst.canonical.currentProps;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import {batchedUpdates} from './legacy-events/ReactGenericBatching';
import accumulateInto from './legacy-events/accumulateInto';

import {getPublicInstance} from './ReactFabricHostConfig';
import getListener from './ReactNativeGetListener';
import {runEventsInBatch} from './legacy-events/EventBatching';

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

Expand Down
179 changes: 36 additions & 143 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,13 @@
* @flow
*/

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

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

import {dispatchEvent} from './ReactFabricEventEmitter';

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

Expand All @@ -49,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 @@ -71,9 +53,18 @@ type Node = Object;
export type Type = string;
export type Props = Object;
export type Instance = {
// Reference to the shadow node.
node: Node,
canonical: ReactFabricHostComponent,
...
// Exposed through refs.
publicInstance: ReactFabricHostComponent,
// We define this as an object instead of as separate fields to simplify
// making copies of instance where `internals` don't change.
internals: {
nativeTag: number,
viewConfig: ViewConfig,
currentProps: Props,
internalInstanceHandle: Object,
},
};
export type TextInstance = {node: Node, ...};
export type HydratableInstance = Instance | TextInstance;
Expand Down Expand Up @@ -107,114 +98,6 @@ if (registerEventHandler) {
registerEventHandler(dispatchEvent);
}

/**
* This is used for refs on host components.
*/
class ReactFabricHostComponent implements NativeMethods {
_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 {stateNode} = this._internalInstanceHandle;
if (stateNode != null) {
fabricMeasure(
stateNode.node,
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
);
}
}

measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
const {stateNode} = this._internalInstanceHandle;
if (stateNode != null) {
fabricMeasureInWindow(
stateNode.node,
mountSafeCallback_NOT_REALLY_SAFE(this, 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 = this._internalInstanceHandle.stateNode;
const fromStateNode =
relativeToNativeNode._internalInstanceHandle.stateNode;

if (toStateNode != null && fromStateNode != null) {
fabricMeasureLayout(
toStateNode.node,
fromStateNode.node,
mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
);
}
}

unstable_getBoundingClientRect(): DOMRect {
const {stateNode} = this._internalInstanceHandle;
if (stateNode != null) {
const rect = fabricGetBoundingClientRect(stateNode.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 {stateNode} = this._internalInstanceHandle;
if (stateNode != null && updatePayload != null) {
setNativeProps(stateNode.node, updatePayload);
}
}
}

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

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

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

Expand Down Expand Up @@ -339,12 +227,15 @@ export function getChildHostContext(
}

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

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

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

Expand Down Expand Up @@ -447,7 +338,8 @@ export function cloneInstance(
}
return {
node: clone,
canonical: instance.canonical,
publicInstance: instance.publicInstance,
internals: instance.internals,
};
}

Expand All @@ -457,15 +349,16 @@ export function cloneHiddenInstance(
props: Props,
internalInstanceHandle: Object,
): Instance {
const viewConfig = instance.canonical.viewConfig;
const viewConfig = instance.internals.viewConfig;
const node = instance.node;
const updatePayload = create(
{style: {display: 'none'}},
viewConfig.validAttributes,
);
return {
node: cloneNodeWithNewProps(node, updatePayload),
canonical: instance.canonical,
publicInstance: instance.publicInstance,
internals: instance.internals,
};
}

Expand Down
Loading

0 comments on commit 5a7d440

Please sign in to comment.