Skip to content

Commit

Permalink
Extract ReactFabricHostComponent into a separate module
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rubennorte authored and facebook-github-bot committed Apr 5, 2023
1 parent 4bff977 commit 1476d99
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 164 deletions.
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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,
);
}

0 comments on commit 1476d99

Please sign in to comment.