Extract ReactFabricHostComponent into a separate module

Summary:
Small refactor in preparation for adding `ReactNativeElement` as an alternative implementation for `ReactFabricHostComponent`.

Changelog: [internal]

bypass-github-export-checks

Reviewed By: yungsters

Differential Revision: D44299619

fbshipit-source-id: b1bc43f6a6ae5b75dca43d7e08cd15acdc49bb79
This commit is contained in:
Rubén Norte
2023-04-05 04:59:46 -07:00
committed by Facebook GitHub Bot
parent 4bff977869
commit 1476d99048
2 changed files with 194 additions and 164 deletions
@@ -0,0 +1,171 @@
/**
* 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.
*
* @format
* @flow strict-local
*/
import type {
AttributeConfiguration,
HostComponent,
INativeMethods,
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
ViewConfig,
} from '../../Renderer/shims/ReactNativeTypes';
import type {ElementRef} from 'react';
import TextInputState from '../../Components/TextInput/TextInputState';
import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric';
import {getFabricUIManager} from '../FabricUIManager';
import {create} from './ReactNativeAttributePayload';
import nullthrows from 'nullthrows';
const {
measure: fabricMeasure,
measureInWindow: fabricMeasureInWindow,
measureLayout: fabricMeasureLayout,
getBoundingClientRect: fabricGetBoundingClientRect,
setNativeProps,
} = nullthrows(getFabricUIManager());
const noop = () => {};
/**
* This is used for refs on host components.
*/
export default class ReactFabricHostComponent implements INativeMethods {
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
__nativeTag: number;
__internalInstanceHandle: mixed;
_viewConfig: ViewConfig;
constructor(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: mixed,
) {
this.__nativeTag = tag;
this._viewConfig = viewConfig;
this.__internalInstanceHandle = internalInstanceHandle;
}
blur() {
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
TextInputState.blurTextInput(this);
}
focus() {
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
TextInputState.focusTextInput(this);
}
measure(callback: MeasureOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null) {
fabricMeasure(node, callback);
}
}
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
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 = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
const fromStateNode = getNodeFromInternalInstanceHandle(
relativeToNativeNode.__internalInstanceHandle,
);
if (toStateNode != null && fromStateNode != null) {
fabricMeasureLayout(
toStateNode,
fromStateNode,
onFail != null ? onFail : noop,
onSuccess != null ? onSuccess : noop,
);
}
}
unstable_getBoundingClientRect(): DOMRect {
const node = getNodeFromInternalInstanceHandle(
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: {...}): void {
if (__DEV__) {
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
}
const updatePayload = create(nativeProps, this._viewConfig.validAttributes);
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null && updatePayload != null) {
setNativeProps(node, updatePayload);
}
}
}
function warnForStyleProps(
props: {...},
validAttributes: AttributeConfiguration,
): void {
if (__DEV__) {
for (const key in validAttributes.style) {
if (!(validAttributes[key] || props[key] === undefined)) {
console.error(
'You are setting the style `{ %s' +
': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { %s' +
': ... } }`',
key,
key,
);
}
}
}
}
@@ -8,173 +8,23 @@
* @flow strict-local
*/
import type {
AttributeConfiguration,
HostComponent,
INativeMethods,
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
ViewConfig,
} from '../../Renderer/shims/ReactNativeTypes';
import type {ElementRef} from 'react';
import typeof ReactFabricType from '../../Renderer/shims/ReactFabric';
import type {ViewConfig} from '../../Renderer/shims/ReactNativeTypes';
import type ReactFabricHostComponentType from './ReactFabricHostComponent';
import TextInputState from '../../Components/TextInput/TextInputState';
import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric';
import {getFabricUIManager} from '../FabricUIManager';
import {create} from './ReactNativeAttributePayload';
import nullthrows from 'nullthrows';
const {
measure: fabricMeasure,
measureInWindow: fabricMeasureInWindow,
measureLayout: fabricMeasureLayout,
getBoundingClientRect: fabricGetBoundingClientRect,
setNativeProps,
} = nullthrows(getFabricUIManager());
const noop = () => {};
/**
* This is used for refs on host components.
*/
export class ReactFabricHostComponent implements INativeMethods {
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
__nativeTag: number;
__internalInstanceHandle: mixed;
_viewConfig: ViewConfig;
constructor(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: mixed,
) {
this.__nativeTag = tag;
this._viewConfig = viewConfig;
this.__internalInstanceHandle = internalInstanceHandle;
}
blur() {
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
TextInputState.blurTextInput(this);
}
focus() {
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
TextInputState.focusTextInput(this);
}
measure(callback: MeasureOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null) {
fabricMeasure(node, callback);
}
}
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
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 = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
const fromStateNode = getNodeFromInternalInstanceHandle(
relativeToNativeNode.__internalInstanceHandle,
);
if (toStateNode != null && fromStateNode != null) {
fabricMeasureLayout(
toStateNode,
fromStateNode,
onFail != null ? onFail : noop,
onSuccess != null ? onSuccess : noop,
);
}
}
unstable_getBoundingClientRect(): DOMRect {
const node = getNodeFromInternalInstanceHandle(
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: {...}): void {
if (__DEV__) {
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
}
const updatePayload = create(nativeProps, this._viewConfig.validAttributes);
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null && updatePayload != null) {
setNativeProps(node, updatePayload);
}
}
}
export function warnForStyleProps(
props: {...},
validAttributes: AttributeConfiguration,
): void {
if (__DEV__) {
for (const key in validAttributes.style) {
if (!(validAttributes[key] || props[key] === undefined)) {
console.error(
'You are setting the style `{ %s' +
': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { %s' +
': ... } }`',
key,
key,
);
}
}
}
}
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
let ReactFabricHostComponent: Class<ReactFabricHostComponentType>;
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
let ReactFabric: ReactFabricType;
export function createPublicInstance(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: mixed,
): ReactFabricHostComponent {
): ReactFabricHostComponentType {
if (ReactFabricHostComponent == null) {
ReactFabricHostComponent = require('./ReactFabricHostComponent').default;
}
return new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
}
@@ -186,15 +36,24 @@ export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
}
export function getNativeTagFromPublicInstance(
publicInstance: ReactFabricHostComponent,
publicInstance: ReactFabricHostComponentType,
): number {
return publicInstance.__nativeTag;
}
export function getNodeFromPublicInstance(
publicInstance: ReactFabricHostComponent,
publicInstance: ReactFabricHostComponentType,
): mixed {
return getNodeFromInternalInstanceHandle(
// Avoid loading ReactFabric if using an instance from the legacy renderer.
if (publicInstance.__internalInstanceHandle == null) {
return null;
}
if (ReactFabric == null) {
ReactFabric = require('../../Renderer/shims/ReactFabric');
}
return ReactFabric.getNodeFromInternalInstanceHandle(
publicInstance.__internalInstanceHandle,
);
}