Skip to content

Commit

Permalink
Clean interface for public instances between React and React Native (#…
Browse files Browse the repository at this point in the history
…26416)

## Summary

We are going to move the definition of public instances from React to
React Native to have them together with the native methods in Fabric
that they invoke. This will allow us to have a better type safety
between them and iterate faster on the implementation of this proposal:
react-native-community/discussions-and-proposals#607

The interface between React and React Native would look like this after
this change and a following PR (#26418):

React → React Native:
```javascript
ReactNativePrivateInterface.createPublicInstance // to provide via refs
ReactNativePrivateInterface.getNodeFromPublicInstance // for DevTools, commands, etc.
ReactNativePrivateInterface.getNativeTagFromPublicInstance // to implement `findNodeHandle`
```

React Native → React (ReactFabric):
```javascript
ReactFabric.getNodeFromInternalInstanceHandle // to get most recent node to call into native
ReactFabric.getPublicInstanceFromInternalInstanceHandle // to get public instances from results from native
```

## How did you test this change?

Flow
Existing unit tests
  • Loading branch information
rubennorte authored Mar 20, 2023
1 parent c57b90f commit 3554c88
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 33 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 @@ -40,6 +40,7 @@ import {
sendAccessibilityEvent,
getNodeFromInternalInstanceHandle,
} from './ReactNativePublicCompat';
import {getPublicInstanceFromInternalInstanceHandle} from './ReactFabricHostConfig';

// $FlowFixMe[missing-local-annot]
function onRecoverableError(error) {
Expand Down Expand Up @@ -124,6 +125,10 @@ export {
// This method allows it to acess the most recent shadow node for
// the instance (it's only accessible through it).
getNodeFromInternalInstanceHandle,
// Fabric native methods to traverse the host tree return the same internal
// instance handles we use to dispatch events. This provides a way to access
// the public instances we created from them (potentially created lazily).
getPublicInstanceFromInternalInstanceHandle,
};

injectIntoDevTools({
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ export function getPublicInstance(instance: Instance): null | PublicInstance {
return null;
}

export function getPublicInstanceFromInternalInstanceHandle(
internalInstanceHandle: Object,
): null | PublicInstance {
const instance: Instance = internalInstanceHandle.stateNode;
return getPublicInstance(instance);
}

export function prepareForCommit(containerInfo: Container): null | Object {
// Noop
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type {ReactFabricHostComponent} from './ReactFabricPublicInstance';
import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat';

/**
* IMPORTANT: This module is used in Paper and Fabric. It needs to be defined
Expand All @@ -22,8 +23,14 @@ export function getNativeTagFromPublicInstance(
return publicInstance.__nativeTag;
}

export function getInternalInstanceHandleFromPublicInstance(
export function getNodeFromPublicInstance(
publicInstance: ReactFabricHostComponent,
): mixed {
return publicInstance.__internalInstanceHandle;
if (publicInstance.__internalInstanceHandle == null) {
return null;
}

return getNodeFromInternalInstanceHandle(
publicInstance.__internalInstanceHandle,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags';
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
import {getInternalInstanceHandleFromPublicInstance} from './ReactFabricPublicInstanceUtils';
import {getNodeFromPublicInstance} from './ReactFabricPublicInstanceUtils';
import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat';

const emptyObject = {};
Expand Down Expand Up @@ -196,12 +196,7 @@ function getInspectorDataForViewAtPoint(
if (__DEV__) {
let closestInstance = null;

const fabricInstanceHandle =
getInternalInstanceHandleFromPublicInstance(inspectedView);
const fabricNode =
fabricInstanceHandle != null
? getNodeFromInternalInstanceHandle(fabricInstanceHandle)
: null;
const fabricNode = getNodeFromPublicInstance(inspectedView);
if (fabricNode) {
// For Fabric we can look up the instance handle directly and measure it.
nativeFabricUIManager.findNodeAtPoint(
Expand Down
22 changes: 7 additions & 15 deletions packages/react-native-renderer/src/ReactNativePublicCompat.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
import getComponentNameFromType from 'shared/getComponentNameFromType';

import {
getInternalInstanceHandleFromPublicInstance,
getNodeFromPublicInstance,
getNativeTagFromPublicInstance,
} from './ReactFabricPublicInstanceUtils';

Expand Down Expand Up @@ -176,14 +176,10 @@ export function dispatchCommand(
return;
}

const internalInstanceHandle =
getInternalInstanceHandleFromPublicInstance(handle);
const node = getNodeFromPublicInstance(handle);

if (internalInstanceHandle != null) {
const node = getNodeFromInternalInstanceHandle(internalInstanceHandle);
if (node != null) {
nativeFabricUIManager.dispatchCommand(node, command, args);
}
if (node != null) {
nativeFabricUIManager.dispatchCommand(node, command, args);
} else {
UIManager.dispatchViewManagerCommand(nativeTag, command, args);
}
Expand All @@ -204,13 +200,9 @@ export function sendAccessibilityEvent(handle: any, eventType: string) {
return;
}

const internalInstanceHandle =
getInternalInstanceHandleFromPublicInstance(handle);
if (internalInstanceHandle != null) {
const node = getNodeFromInternalInstanceHandle(internalInstanceHandle);
if (node != null) {
nativeFabricUIManager.sendAccessibilityEvent(node, eventType);
}
const node = getNodeFromPublicInstance(handle);
if (node != null) {
nativeFabricUIManager.sendAccessibilityEvent(node, eventType);
} else {
legacySendAccessibilityEvent(nativeTag, eventType);
}
Expand Down
9 changes: 8 additions & 1 deletion packages/react-native-renderer/src/ReactNativeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ export type ReactNativeType = {
};

export opaque type Node = mixed;
type InternalInstanceHandle = mixed;
type PublicInstance = mixed;

export type ReactFabricType = {
findHostInstance_DEPRECATED<TElementType: ElementType>(
Expand All @@ -237,7 +239,12 @@ export type ReactFabricType = {
concurrentRoot: ?boolean,
): ?ElementRef<ElementType>,
unmountComponentAtNode(containerTag: number): void,
getNodeFromInternalInstanceHandle(internalInstanceHandle: mixed): ?Node,
getNodeFromInternalInstanceHandle(
internalInstanceHandle: InternalInstanceHandle,
): ?Node,
getPublicInstanceFromInternalInstanceHandle(
internalInstanceHandle: InternalInstanceHandle,
): PublicInstance,
...
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let createReactNativeComponentClass;
let StrictMode;
let act;
let getNativeTagFromPublicInstance;
let getInternalInstanceHandleFromPublicInstance;
let getNodeFromPublicInstance;

const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
"Warning: dispatchCommand was called with a ref that isn't a " +
Expand Down Expand Up @@ -44,8 +44,8 @@ describe('ReactFabric', () => {
.ReactNativeViewConfigRegistry.register;
getNativeTagFromPublicInstance =
require('../ReactFabricPublicInstanceUtils').getNativeTagFromPublicInstance;
getInternalInstanceHandleFromPublicInstance =
require('../ReactFabricPublicInstanceUtils').getInternalInstanceHandleFromPublicInstance;
getNodeFromPublicInstance =
require('../ReactFabricPublicInstanceUtils').getNodeFromPublicInstance;

act = require('internal-test-utils').act;
});
Expand Down Expand Up @@ -1032,10 +1032,7 @@ describe('ReactFabric', () => {
nativeFabricUIManager.createNode.mock.results[0].value;
expect(expectedShadowNode).toEqual(expect.any(Object));

const internalInstanceHandle =
getInternalInstanceHandleFromPublicInstance(viewRef);
expect(
ReactFabric.getNodeFromInternalInstanceHandle(internalInstanceHandle),
).toBe(expectedShadowNode);
const node = getNodeFromPublicInstance(viewRef);
expect(node).toBe(expectedShadowNode);
});
});

0 comments on commit 3554c88

Please sign in to comment.