Skip to content

Commit

Permalink
Extract duplicated methods in Fabric and the legacy renderer to a sha…
Browse files Browse the repository at this point in the history
…red module (#26319)

## Summary

The following methods have exactly the same implementation on Fabric and
the legacy renderer:
* `findHostInstance_DEPRECATED`
* `findNodeHandle`
* `dispatchCommand`
* `sendAccessibilityEvent`

This just extracts those functions to a common module so they're easier
to change (no need to sync changes in 2 files).

## How did you test this change?

Existing tests (this is a refactor).
  • Loading branch information
rubennorte committed Mar 6, 2023
1 parent 49f7410 commit eb616a1
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 292 deletions.
155 changes: 6 additions & 149 deletions packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@
* @flow
*/

import type {HostComponent} from './ReactNativeTypes';
import type {ReactPortal, ReactNodeList} from 'shared/ReactTypes';
import type {ElementRef, Element, ElementType} from 'react';
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';

import './ReactFabricInjection';

import {
findHostInstance,
findHostInstanceWithWarning,
batchedUpdates as batchedUpdatesImpl,
discreteUpdates,
createContainer,
Expand All @@ -29,159 +26,19 @@ import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal
import {setBatchingImplementation} from './legacy-events/ReactGenericBatching';
import ReactVersion from 'shared/ReactVersion';

// Modules provided by RN:
import {
UIManager,
legacySendAccessibilityEvent,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
import {
getInspectorDataForViewTag,
getInspectorDataForViewAtPoint,
getInspectorDataForInstance,
} from './ReactNativeFiberInspector';
import {LegacyRoot, ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import getComponentNameFromType from 'shared/getComponentNameFromType';

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;

function findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?ElementRef<HostComponent<mixed>> {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
if (!owner.stateNode._warnedAboutRefsInRender) {
console.error(
'%s is accessing findNodeHandle inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentNameFromType(owner.type) || 'A component',
);
}

owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
if (componentOrHandle._nativeTag) {
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
return componentOrHandle;
}
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
return componentOrHandle.canonical;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findHostInstance_DEPRECATED',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

return hostInstance;
}

function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
if (!owner.stateNode._warnedAboutRefsInRender) {
console.error(
'%s is accessing findNodeHandle inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentNameFromType(owner.type) || 'A component',
);
}

owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
if (typeof componentOrHandle === 'number') {
// Already a node handle
return componentOrHandle;
}
if (componentOrHandle._nativeTag) {
return componentOrHandle._nativeTag;
}
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
return componentOrHandle.canonical._nativeTag;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findNodeHandle',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

if (hostInstance == null) {
return hostInstance;
}

return hostInstance._nativeTag;
}

function dispatchCommand(handle: any, command: string, args: Array<any>) {
if (handle._nativeTag == null) {
if (__DEV__) {
console.error(
"dispatchCommand was called with a ref that isn't a " +
'native component. Use React.forwardRef to get access to the underlying native component',
);
}
return;
}

if (handle._internalInstanceHandle != null) {
const {stateNode} = handle._internalInstanceHandle;
if (stateNode != null) {
nativeFabricUIManager.dispatchCommand(stateNode.node, command, args);
}
} else {
UIManager.dispatchViewManagerCommand(handle._nativeTag, command, args);
}
}

function sendAccessibilityEvent(handle: any, eventType: string) {
if (handle._nativeTag == null) {
if (__DEV__) {
console.error(
"sendAccessibilityEvent was called with a ref that isn't a " +
'native component. Use React.forwardRef to get access to the underlying native component',
);
}
return;
}

if (handle._internalInstanceHandle != null) {
const {stateNode} = handle._internalInstanceHandle;
if (stateNode != null) {
nativeFabricUIManager.sendAccessibilityEvent(stateNode.node, eventType);
}
} else {
legacySendAccessibilityEvent(handle._nativeTag, eventType);
}
}
import {
findHostInstance_DEPRECATED,
findNodeHandle,
dispatchCommand,
sendAccessibilityEvent,
} from './ReactNativePublicCompat';

// $FlowFixMe[missing-local-annot]
function onRecoverableError(error) {
Expand Down
166 changes: 166 additions & 0 deletions packages/react-native-renderer/src/ReactNativePublicCompat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {HostComponent} from './ReactNativeTypes';
import type {ElementRef, ElementType} from 'react';

// Modules provided by RN:
import {
UIManager,
legacySendAccessibilityEvent,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {
findHostInstance,
findHostInstanceWithWarning,
} from 'react-reconciler/src/ReactFiberReconciler';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import getComponentNameFromType from 'shared/getComponentNameFromType';

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;

export function findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?ElementRef<HostComponent<mixed>> {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
if (!owner.stateNode._warnedAboutRefsInRender) {
console.error(
'%s is accessing findNodeHandle inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentNameFromType(owner.type) || 'A component',
);
}

owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
if (componentOrHandle._nativeTag) {
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
return componentOrHandle;
}
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
// $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN
return componentOrHandle.canonical;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findHostInstance_DEPRECATED',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

return hostInstance;
}

export function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
if (!owner.stateNode._warnedAboutRefsInRender) {
console.error(
'%s is accessing findNodeHandle inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentNameFromType(owner.type) || 'A component',
);
}

owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
if (typeof componentOrHandle === 'number') {
// Already a node handle
return componentOrHandle;
}
if (componentOrHandle._nativeTag) {
return componentOrHandle._nativeTag;
}
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
return componentOrHandle.canonical._nativeTag;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findNodeHandle',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}

if (hostInstance == null) {
return hostInstance;
}

return hostInstance._nativeTag;
}

export function dispatchCommand(
handle: any,
command: string,
args: Array<any>,
) {
if (handle._nativeTag == null) {
if (__DEV__) {
console.error(
"dispatchCommand was called with a ref that isn't a " +
'native component. Use React.forwardRef to get access to the underlying native component',
);
}
return;
}

if (handle._internalInstanceHandle != null) {
const {stateNode} = handle._internalInstanceHandle;
if (stateNode != null) {
nativeFabricUIManager.dispatchCommand(stateNode.node, command, args);
}
} else {
UIManager.dispatchViewManagerCommand(handle._nativeTag, command, args);
}
}

export function sendAccessibilityEvent(handle: any, eventType: string) {
if (handle._nativeTag == null) {
if (__DEV__) {
console.error(
"sendAccessibilityEvent was called with a ref that isn't a " +
'native component. Use React.forwardRef to get access to the underlying native component',
);
}
return;
}

if (handle._internalInstanceHandle != null) {
const {stateNode} = handle._internalInstanceHandle;
if (stateNode != null) {
nativeFabricUIManager.sendAccessibilityEvent(stateNode.node, eventType);
}
} else {
legacySendAccessibilityEvent(handle._nativeTag, eventType);
}
}
Loading

0 comments on commit eb616a1

Please sign in to comment.