mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Native] Delete NativeComponent and NativeMethodsMixin (#18036)
* [Native] Delete NativeComponent and NativeMethodsMixin * Remove more files
This commit is contained in:
@@ -1,330 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 {
|
||||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
NativeMethodsMixinType,
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from './ReactNativeTypes';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
// Modules provided by RN:
|
||||
import {
|
||||
TextInputState,
|
||||
UIManager,
|
||||
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import {create} from './ReactNativeAttributePayload';
|
||||
import {
|
||||
mountSafeCallback_NOT_REALLY_SAFE,
|
||||
throwOnStylesProp,
|
||||
warnForStyleProps,
|
||||
} from './NativeMethodsMixinUtils';
|
||||
|
||||
export default function(
|
||||
findNodeHandle: any => ?number,
|
||||
findHostInstance: any => any,
|
||||
) {
|
||||
/**
|
||||
* `NativeMethodsMixin` provides methods to access the underlying native
|
||||
* component directly. This can be useful in cases when you want to focus
|
||||
* a view or measure its on-screen dimensions, for example.
|
||||
*
|
||||
* The methods described here are available on most of the default components
|
||||
* provided by React Native. Note, however, that they are *not* available on
|
||||
* composite components that aren't directly backed by a native view. This will
|
||||
* generally include most components that you define in your own app. For more
|
||||
* information, see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html).
|
||||
*
|
||||
* Note the Flow $Exact<> syntax is required to support mixins.
|
||||
* React createClass mixins can only be used with exact types.
|
||||
*/
|
||||
const NativeMethodsMixin: $Exact<NativeMethodsMixinType> = {
|
||||
/**
|
||||
* Determines the location on screen, width, and height of the given view and
|
||||
* returns the values via an async callback. If successful, the callback will
|
||||
* be called with the following arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
* - pageX
|
||||
* - pageY
|
||||
*
|
||||
* Note that these measurements are not available until after the rendering
|
||||
* has been completed in native. If you need the measurements as soon as
|
||||
* possible, consider using the [`onLayout`
|
||||
* prop](docs/view.html#onlayout) instead.
|
||||
*/
|
||||
measure: function(callback: MeasureOnSuccessCallback) {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
// We can't call FabricUIManager here because it won't be loaded in paper
|
||||
// at initialization time. See https://github.com/facebook/react/pull/15490
|
||||
// for more info.
|
||||
nativeFabricUIManager.measure(
|
||||
maybeInstance.node,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
} else {
|
||||
UIManager.measure(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the location of the given view in the window and returns the
|
||||
* values via an async callback. If the React root view is embedded in
|
||||
* another native view, this will give you the absolute coordinates. If
|
||||
* successful, the callback will be called with the following
|
||||
* arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
*
|
||||
* Note that these measurements are not available until after the rendering
|
||||
* has been completed in native.
|
||||
*/
|
||||
measureInWindow: function(callback: MeasureInWindowOnSuccessCallback) {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
// We can't call FabricUIManager here because it won't be loaded in paper
|
||||
// at initialization time. See https://github.com/facebook/react/pull/15490
|
||||
// for more info.
|
||||
nativeFabricUIManager.measureInWindow(
|
||||
maybeInstance.node,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
} else {
|
||||
UIManager.measureInWindow(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Like [`measure()`](#measure), but measures the view relative an ancestor,
|
||||
* specified as `relativeToNativeNode`. This means that the returned x, y
|
||||
* are relative to the origin x, y of the ancestor view.
|
||||
*
|
||||
* As always, to obtain a native node handle for a component, you can use
|
||||
* `findNodeHandle(component)`.
|
||||
*/
|
||||
measureLayout: function(
|
||||
relativeToNativeNode: number | Object,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail?: () => void /* currently unused */,
|
||||
) {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: measureLayout on components using NativeMethodsMixin ' +
|
||||
'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
|
||||
'measureLayout must be called on a native ref. Consider using forwardRef.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
let relativeNode;
|
||||
|
||||
if (typeof relativeToNativeNode === 'number') {
|
||||
// Already a node handle
|
||||
relativeNode = relativeToNativeNode;
|
||||
} else if (relativeToNativeNode._nativeTag) {
|
||||
relativeNode = relativeToNativeNode._nativeTag;
|
||||
}
|
||||
|
||||
if (relativeNode == null) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UIManager.measureLayout(
|
||||
findNodeHandle(this),
|
||||
relativeNode,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function sends props straight to native. They will not participate in
|
||||
* future diff process - this means that if you do not include them in the
|
||||
* next render, they will remain active (see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html)).
|
||||
*/
|
||||
setNativeProps: function(nativeProps: Object) {
|
||||
// Class components don't have viewConfig -> validateAttributes.
|
||||
// Nor does it make sense to set native props on a non-native component.
|
||||
// Instead, find the nearest host component and set props on it.
|
||||
// Use findNodeHandle() rather than findNodeHandle() because
|
||||
// We want the instance/wrapper (not the native tag).
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: setNativeProps is not currently supported in Fabric',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const nativeTag =
|
||||
maybeInstance._nativeTag || maybeInstance.canonical._nativeTag;
|
||||
const viewConfig: ReactNativeBaseComponentViewConfig<> =
|
||||
maybeInstance.viewConfig || maybeInstance.canonical.viewConfig;
|
||||
|
||||
if (__DEV__) {
|
||||
warnForStyleProps(nativeProps, viewConfig.validAttributes);
|
||||
}
|
||||
|
||||
const updatePayload = create(nativeProps, viewConfig.validAttributes);
|
||||
|
||||
// Avoid the overhead of bridge calls if there's no update.
|
||||
// This is an expensive no-op for Android, and causes an unnecessary
|
||||
// view invalidation for certain components (eg RCTTextInput) on iOS.
|
||||
if (updatePayload != null) {
|
||||
UIManager.updateView(
|
||||
nativeTag,
|
||||
viewConfig.uiViewClassName,
|
||||
updatePayload,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests focus for the given input or view. The exact behavior triggered
|
||||
* will depend on the platform and type of view.
|
||||
*/
|
||||
focus: function() {
|
||||
TextInputState.focusTextInput(findNodeHandle(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
*/
|
||||
blur: function() {
|
||||
TextInputState.blurTextInput(findNodeHandle(this));
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
// hide this from Flow since we can't define these properties outside of
|
||||
// __DEV__ without actually implementing them (setting them to undefined
|
||||
// isn't allowed by ReactClass)
|
||||
const NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
|
||||
invariant(
|
||||
!NativeMethodsMixin_DEV.componentWillMount &&
|
||||
!NativeMethodsMixin_DEV.componentWillReceiveProps &&
|
||||
!NativeMethodsMixin_DEV.UNSAFE_componentWillMount &&
|
||||
!NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps,
|
||||
'Do not override existing functions.',
|
||||
);
|
||||
// TODO (bvaughn) Remove cWM and cWRP in a future version of React Native,
|
||||
// Once these lifecycles have been remove from the reconciler.
|
||||
NativeMethodsMixin_DEV.componentWillMount = function() {
|
||||
throwOnStylesProp(this, this.props);
|
||||
};
|
||||
NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
NativeMethodsMixin_DEV.UNSAFE_componentWillMount = function() {
|
||||
throwOnStylesProp(this, this.props);
|
||||
};
|
||||
NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps = function(
|
||||
newProps,
|
||||
) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
|
||||
// React may warn about cWM/cWRP/cWU methods being deprecated.
|
||||
// Add a flag to suppress these warnings for this special case.
|
||||
// TODO (bvaughn) Remove this flag once the above methods have been removed.
|
||||
NativeMethodsMixin_DEV.componentWillMount.__suppressDeprecationWarning = true;
|
||||
NativeMethodsMixin_DEV.componentWillReceiveProps.__suppressDeprecationWarning = true;
|
||||
}
|
||||
|
||||
return NativeMethodsMixin;
|
||||
}
|
||||
+1
-8
@@ -32,8 +32,6 @@ import ReactVersion from 'shared/ReactVersion';
|
||||
// Module provided by RN:
|
||||
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import NativeMethodsMixin from './NativeMethodsMixin';
|
||||
import ReactNativeComponent from './ReactNativeComponent';
|
||||
import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
|
||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||
|
||||
@@ -155,8 +153,6 @@ setBatchingImplementation(
|
||||
const roots = new Map();
|
||||
|
||||
const ReactFabric: ReactFabricType = {
|
||||
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
|
||||
|
||||
// This is needed for implementation details of TouchableNativeFeedback
|
||||
// Remove this once TouchableNativeFeedback doesn't use cloneElement
|
||||
findHostInstance_DEPRECATED,
|
||||
@@ -223,10 +219,7 @@ const ReactFabric: ReactFabricType = {
|
||||
return createPortal(children, containerTag, null, key);
|
||||
},
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Used as a mixin in many createClass-based components
|
||||
NativeMethodsMixin: NativeMethodsMixin(findNodeHandle, findHostInstance),
|
||||
},
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {},
|
||||
};
|
||||
|
||||
injectIntoDevTools({
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {
|
||||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
NativeMethods,
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from './ReactNativeTypes';
|
||||
|
||||
import React from 'react';
|
||||
// Modules provided by RN:
|
||||
import {
|
||||
TextInputState,
|
||||
UIManager,
|
||||
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import {create} from './ReactNativeAttributePayload';
|
||||
import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
|
||||
|
||||
export default function(
|
||||
findNodeHandle: any => ?number,
|
||||
findHostInstance: any => any,
|
||||
) {
|
||||
/**
|
||||
* Superclass that provides methods to access the underlying native component.
|
||||
* This can be useful when you want to focus a view or measure its dimensions.
|
||||
*
|
||||
* Methods implemented by this class are available on most default components
|
||||
* provided by React Native. However, they are *not* available on composite
|
||||
* components that are not directly backed by a native view. For more
|
||||
* information, see [Direct Manipulation](docs/direct-manipulation.html).
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
class ReactNativeComponent<Props> extends React.Component<Props> {
|
||||
/**
|
||||
* Due to bugs in Flow's handling of React.createClass, some fields already
|
||||
* declared in the base class need to be redeclared below.
|
||||
*/
|
||||
props: Props;
|
||||
|
||||
/**
|
||||
* Removes focus. This is the opposite of `focus()`.
|
||||
*/
|
||||
blur(): void {
|
||||
TextInputState.blurTextInput(findNodeHandle(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests focus. The exact behavior depends on the platform and view.
|
||||
*/
|
||||
focus(): void {
|
||||
TextInputState.focusTextInput(findNodeHandle(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the on-screen location and dimensions. If successful, the callback
|
||||
* will be called asynchronously with the following arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
* - pageX
|
||||
* - pageY
|
||||
*
|
||||
* These values are not available until after natives rendering completes. If
|
||||
* you need the measurements as soon as possible, consider using the
|
||||
* [`onLayout` prop](docs/view.html#onlayout) instead.
|
||||
*/
|
||||
measure(callback: MeasureOnSuccessCallback): void {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
// We can't call FabricUIManager here because it won't be loaded in paper
|
||||
// at initialization time. See https://github.com/facebook/react/pull/15490
|
||||
// for more info.
|
||||
nativeFabricUIManager.measure(
|
||||
maybeInstance.node,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
} else {
|
||||
UIManager.measure(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the on-screen location and dimensions. Even if the React Native
|
||||
* root view is embedded within another native view, this method will give you
|
||||
* the absolute coordinates measured from the window. If successful, the
|
||||
* callback will be called asynchronously with the following arguments:
|
||||
*
|
||||
* - x
|
||||
* - y
|
||||
* - width
|
||||
* - height
|
||||
*
|
||||
* These values are not available until after natives rendering completes.
|
||||
*/
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
// We can't call FabricUIManager here because it won't be loaded in paper
|
||||
// at initialization time. See https://github.com/facebook/react/pull/15490
|
||||
// for more info.
|
||||
nativeFabricUIManager.measureInWindow(
|
||||
maybeInstance.node,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
} else {
|
||||
UIManager.measureInWindow(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, callback),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to [`measure()`](#measure), but the resulting location will be
|
||||
* relative to the supplied ancestor's location.
|
||||
*
|
||||
* Obtain a native node handle with `ReactNative.findNodeHandle(component)`.
|
||||
*/
|
||||
measureLayout(
|
||||
relativeToNativeNode: number | Object,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail?: () => void /* currently unused */,
|
||||
): void {
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: measureLayout on components using NativeMethodsMixin ' +
|
||||
'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
|
||||
'measureLayout must be called on a native ref. Consider using forwardRef.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
let relativeNode;
|
||||
|
||||
if (typeof relativeToNativeNode === 'number') {
|
||||
// Already a node handle
|
||||
relativeNode = relativeToNativeNode;
|
||||
} else if (relativeToNativeNode._nativeTag) {
|
||||
relativeNode = relativeToNativeNode._nativeTag;
|
||||
}
|
||||
|
||||
if (relativeNode == null) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UIManager.measureLayout(
|
||||
findNodeHandle(this),
|
||||
relativeNode,
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, onFail),
|
||||
mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends props straight to native. They will not participate in
|
||||
* future diff process - this means that if you do not include them in the
|
||||
* next render, they will remain active (see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html)).
|
||||
*/
|
||||
setNativeProps(nativeProps: Object): void {
|
||||
// Class components don't have viewConfig -> validateAttributes.
|
||||
// Nor does it make sense to set native props on a non-native component.
|
||||
// Instead, find the nearest host component and set props on it.
|
||||
// Use findNodeHandle() rather than ReactNative.findNodeHandle() because
|
||||
// We want the instance/wrapper (not the native tag).
|
||||
let maybeInstance;
|
||||
|
||||
// Fiber errors if findNodeHandle is called for an umounted component.
|
||||
// Tests using ReactTestRenderer will trigger this case indirectly.
|
||||
// Mimicking stack behavior, we should silently ignore this case.
|
||||
// TODO Fix ReactTestRenderer so we can remove this try/catch.
|
||||
try {
|
||||
maybeInstance = findHostInstance(this);
|
||||
} catch (error) {}
|
||||
|
||||
// If there is no host component beneath this we should fail silently.
|
||||
// This is not an error; it could mean a class component rendered null.
|
||||
if (maybeInstance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: setNativeProps is not currently supported in Fabric',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const nativeTag =
|
||||
maybeInstance._nativeTag || maybeInstance.canonical._nativeTag;
|
||||
const viewConfig: ReactNativeBaseComponentViewConfig<> =
|
||||
maybeInstance.viewConfig || maybeInstance.canonical.viewConfig;
|
||||
|
||||
const updatePayload = create(nativeProps, viewConfig.validAttributes);
|
||||
|
||||
// Avoid the overhead of bridge calls if there's no update.
|
||||
// This is an expensive no-op for Android, and causes an unnecessary
|
||||
// view invalidation for certain components (eg RCTTextInput) on iOS.
|
||||
if (updatePayload != null) {
|
||||
UIManager.updateView(
|
||||
nativeTag,
|
||||
viewConfig.uiViewClassName,
|
||||
updatePayload,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
(ReactNativeComponent.prototype: NativeMethods);
|
||||
|
||||
return ReactNativeComponent;
|
||||
}
|
||||
@@ -28,13 +28,6 @@ import {
|
||||
warnForStyleProps,
|
||||
} from './NativeMethodsMixinUtils';
|
||||
|
||||
/**
|
||||
* This component defines the same methods as NativeMethodsMixin but without the
|
||||
* findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views
|
||||
* and would also result in a circular require.js dependency (since
|
||||
* ReactNativeFiber depends on this component and NativeMethodsMixin depends on
|
||||
* ReactNativeFiber).
|
||||
*/
|
||||
class ReactNativeFiberHostComponent {
|
||||
_children: Array<Instance | number>;
|
||||
_nativeTag: number;
|
||||
|
||||
@@ -35,8 +35,6 @@ import ReactVersion from 'shared/ReactVersion';
|
||||
// Module provided by RN:
|
||||
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import NativeMethodsMixin from './NativeMethodsMixin';
|
||||
import ReactNativeComponent from './ReactNativeComponent';
|
||||
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||
|
||||
@@ -164,8 +162,6 @@ function computeComponentStackForErrorReporting(reactTag: number): string {
|
||||
const roots = new Map();
|
||||
|
||||
const ReactNativeRenderer: ReactNativeType = {
|
||||
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
|
||||
|
||||
// This is needed for implementation details of TouchableNativeFeedback
|
||||
// Remove this once TouchableNativeFeedback doesn't use cloneElement
|
||||
findHostInstance_DEPRECATED,
|
||||
@@ -235,8 +231,6 @@ const ReactNativeRenderer: ReactNativeType = {
|
||||
unstable_batchedUpdates: batchedUpdates,
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Used as a mixin in many createClass-based components
|
||||
NativeMethodsMixin: NativeMethodsMixin(findNodeHandle, findHostInstance),
|
||||
computeComponentStackForErrorReporting,
|
||||
},
|
||||
};
|
||||
|
||||
+2
-34
@@ -8,7 +8,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, {type ElementRef, type AbstractComponent} from 'react';
|
||||
import type {ElementRef, AbstractComponent} from 'react';
|
||||
|
||||
export type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
@@ -77,31 +77,6 @@ export type ReactNativeBaseComponentViewConfig<
|
||||
|
||||
export type ViewConfigGetter = () => ReactNativeBaseComponentViewConfig<>;
|
||||
|
||||
/**
|
||||
* Class only exists for its Flow type.
|
||||
*/
|
||||
class ReactNativeComponent<Props> extends React.Component<Props> {
|
||||
blur(): void {}
|
||||
focus(): void {}
|
||||
measure(callback: MeasureOnSuccessCallback): void {}
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {}
|
||||
measureLayout(
|
||||
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail?: () => void,
|
||||
): void {}
|
||||
setNativeProps(nativeProps: Object): void {}
|
||||
}
|
||||
|
||||
// This type is only used for FlowTests. It shouldn't be imported directly
|
||||
export type _InternalReactNativeComponentClass<Props> = Class<
|
||||
ReactNativeComponent<Props>,
|
||||
>;
|
||||
|
||||
/**
|
||||
* This type keeps ReactNativeFiberHostComponent and NativeMethodsMixin in sync.
|
||||
* It can also provide types for ReactNative applications that use NMM or refs.
|
||||
*/
|
||||
export type NativeMethods = {
|
||||
blur(): void,
|
||||
focus(): void,
|
||||
@@ -116,28 +91,22 @@ export type NativeMethods = {
|
||||
...
|
||||
};
|
||||
|
||||
export type NativeMethodsMixinType = NativeMethods;
|
||||
export type HostComponent<T> = AbstractComponent<T, $ReadOnly<NativeMethods>>;
|
||||
|
||||
type SecretInternalsType = {
|
||||
NativeMethodsMixin: NativeMethodsMixinType,
|
||||
computeComponentStackForErrorReporting(tag: number): string,
|
||||
// TODO (bvaughn) Decide which additional types to expose here?
|
||||
// And how much information to fill in for the above types.
|
||||
...
|
||||
};
|
||||
|
||||
type SecretInternalsFabricType = {
|
||||
NativeMethodsMixin: NativeMethodsMixinType,
|
||||
...
|
||||
};
|
||||
type SecretInternalsFabricType = {...};
|
||||
|
||||
/**
|
||||
* Flat ReactNative renderer bundles are too big for Flow to parse efficiently.
|
||||
* Provide minimal Flow typing for the high-level RN API and call it a day.
|
||||
*/
|
||||
export type ReactNativeType = {
|
||||
NativeComponent: typeof ReactNativeComponent,
|
||||
findHostInstance_DEPRECATED(
|
||||
componentOrHandle: any,
|
||||
): ?ElementRef<HostComponent<mixed>>,
|
||||
@@ -157,7 +126,6 @@ export type ReactNativeType = {
|
||||
};
|
||||
|
||||
export type ReactFabricType = {
|
||||
NativeComponent: typeof ReactNativeComponent,
|
||||
findHostInstance_DEPRECATED(componentOrHandle: any): ?HostComponent<mixed>,
|
||||
findNodeHandle(componentOrHandle: any): ?number,
|
||||
dispatchCommand(handle: any, command: string, args: Array<any>): void,
|
||||
|
||||
@@ -13,11 +13,9 @@
|
||||
let React;
|
||||
let ReactFabric;
|
||||
let ReactFeatureFlags;
|
||||
let createReactClass;
|
||||
let createReactNativeComponentClass;
|
||||
let UIManager;
|
||||
let StrictMode;
|
||||
let NativeMethodsMixin;
|
||||
|
||||
const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
|
||||
'Warning: setNativeProps is not currently supported in Fabric';
|
||||
@@ -42,16 +40,8 @@ describe('ReactFabric', () => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.UIManager;
|
||||
createReactClass = require('create-react-class/factory')(
|
||||
React.Component,
|
||||
React.isValidElement,
|
||||
new React.Component().updater,
|
||||
);
|
||||
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.ReactNativeViewConfigRegistry.register;
|
||||
NativeMethodsMixin =
|
||||
ReactFabric.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.NativeMethodsMixin;
|
||||
});
|
||||
|
||||
it('should be able to create and render a native component', () => {
|
||||
@@ -213,49 +203,34 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactFabric.NativeComponent {
|
||||
render() {
|
||||
return <View />;
|
||||
}
|
||||
}
|
||||
UIManager.updateView.mockReset();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render: () => {
|
||||
return <View />;
|
||||
},
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.updateView.mockReset();
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({foo: 'baz'});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({foo: 'baz'});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should call dispatchCommand for native refs', () => {
|
||||
@@ -264,75 +239,53 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
[View].forEach(Component => {
|
||||
nativeFabricUIManager.dispatchCommand.mockClear();
|
||||
nativeFabricUIManager.dispatchCommand.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
ReactFabric.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
expect(nativeFabricUIManager.dispatchCommand).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
nativeFabricUIManager.dispatchCommand,
|
||||
).toHaveBeenCalledWith(expect.any(Object), 'updateCommand', [10, 20]);
|
||||
});
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
ReactFabric.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
expect(nativeFabricUIManager.dispatchCommand).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
nativeFabricUIManager.dispatchCommand,
|
||||
).toHaveBeenCalledWith(expect.any(Object), 'updateCommand', [10, 20]);
|
||||
});
|
||||
|
||||
it('should warn and no-op if calling dispatchCommand on non native refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class BasicClass extends React.Component {
|
||||
render() {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
}
|
||||
|
||||
class Subclass extends ReactFabric.NativeComponent {
|
||||
render() {
|
||||
return <View />;
|
||||
}
|
||||
}
|
||||
nativeFabricUIManager.dispatchCommand.mockReset();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render: () => {
|
||||
return <View />;
|
||||
},
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<BasicClass
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
expect(() => {
|
||||
ReactFabric.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
}).toErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
[BasicClass, Subclass, CreateClass].forEach(Component => {
|
||||
nativeFabricUIManager.dispatchCommand.mockReset();
|
||||
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
expect(() => {
|
||||
ReactFabric.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
}).toErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
});
|
||||
expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should call FabricUIManager.measure on ref.measure', () => {
|
||||
@@ -341,39 +294,24 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactFabric.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
nativeFabricUIManager.measure.mockClear();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
nativeFabricUIManager.measure.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measure(successCallback);
|
||||
expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
|
||||
});
|
||||
expect(nativeFabricUIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measure(successCallback);
|
||||
expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
|
||||
});
|
||||
|
||||
it('should call FabricUIManager.measureInWindow on ref.measureInWindow', () => {
|
||||
@@ -382,39 +320,24 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactFabric.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
nativeFabricUIManager.measureInWindow.mockClear();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
nativeFabricUIManager.measureInWindow.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactFabric.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measureInWindow(successCallback);
|
||||
expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
|
||||
});
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measureInWindow(successCallback);
|
||||
expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
|
||||
});
|
||||
|
||||
it('should support ref in ref.measureLayout', () => {
|
||||
@@ -423,98 +346,34 @@ describe('ReactFabric', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
[View].forEach(Component => {
|
||||
nativeFabricUIManager.measureLayout.mockClear();
|
||||
nativeFabricUIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactFabric.render(
|
||||
<Component>
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</Component>,
|
||||
11,
|
||||
);
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
});
|
||||
|
||||
it('should warn when calling measureLayout on Subclass and NativeMethodsMixin', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactFabric.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
|
||||
[Subclass, CreateClass].forEach(Component => {
|
||||
nativeFabricUIManager.measureLayout.mockReset();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactFabric.render(
|
||||
<Component>
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</Component>,
|
||||
11,
|
||||
);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
|
||||
expect(() => {
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
}).toErrorDev(
|
||||
[
|
||||
'Warning: measureLayout on components using NativeMethodsMixin ' +
|
||||
'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
|
||||
'measureLayout must be called on a native ref. Consider using forwardRef.',
|
||||
],
|
||||
{
|
||||
withoutStack: true,
|
||||
},
|
||||
);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
expect(UIManager.measureLayout).not.toBeCalled();
|
||||
});
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
|
||||
it('returns the correct instance and calls it in the callback', () => {
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
let React;
|
||||
let StrictMode;
|
||||
let ReactNative;
|
||||
let createReactClass;
|
||||
let createReactNativeComponentClass;
|
||||
let UIManager;
|
||||
let NativeMethodsMixin;
|
||||
|
||||
const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
|
||||
"Warning: dispatchCommand was called with a ref that isn't a " +
|
||||
@@ -31,16 +29,8 @@ describe('ReactNative', () => {
|
||||
ReactNative = require('react-native-renderer');
|
||||
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.UIManager;
|
||||
createReactClass = require('create-react-class/factory')(
|
||||
React.Component,
|
||||
React.isValidElement,
|
||||
new React.Component().updater,
|
||||
);
|
||||
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.ReactNativeViewConfigRegistry.register;
|
||||
NativeMethodsMixin =
|
||||
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.NativeMethodsMixin;
|
||||
});
|
||||
|
||||
it('should be able to create and render a native component', () => {
|
||||
@@ -109,75 +99,53 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
[View].forEach(Component => {
|
||||
UIManager.dispatchViewManagerCommand.mockClear();
|
||||
UIManager.dispatchViewManagerCommand.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
UIManager.dispatchViewManagerCommand,
|
||||
).toHaveBeenCalledWith(expect.any(Number), 'updateCommand', [10, 20]);
|
||||
});
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
UIManager.dispatchViewManagerCommand,
|
||||
).toHaveBeenCalledWith(expect.any(Number), 'updateCommand', [10, 20]);
|
||||
});
|
||||
|
||||
it('should warn and no-op if calling dispatchCommand on non native refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class BasicClass extends React.Component {
|
||||
render() {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
}
|
||||
|
||||
class Subclass extends ReactNative.NativeComponent {
|
||||
render() {
|
||||
return <View />;
|
||||
}
|
||||
}
|
||||
UIManager.dispatchViewManagerCommand.mockReset();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render: () => {
|
||||
return <View />;
|
||||
},
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<BasicClass
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
expect(() => {
|
||||
ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
}).toErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
[BasicClass, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.dispatchViewManagerCommand.mockReset();
|
||||
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
expect(() => {
|
||||
ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]);
|
||||
}).toErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
});
|
||||
expect(UIManager.dispatchViewManagerCommand).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => {
|
||||
@@ -186,46 +154,31 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactNative.NativeComponent {
|
||||
render() {
|
||||
return <View />;
|
||||
}
|
||||
}
|
||||
UIManager.updateView.mockReset();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render: () => {
|
||||
return <View />;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.updateView.mockReset();
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
viewRef.setNativeProps({});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
viewRef.setNativeProps({});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
viewRef.setNativeProps({foo: 'baz'});
|
||||
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
|
||||
expect(UIManager.updateView).toHaveBeenCalledWith(
|
||||
expect.any(Number),
|
||||
'RCTView',
|
||||
{foo: 'baz'},
|
||||
);
|
||||
});
|
||||
viewRef.setNativeProps({foo: 'baz'});
|
||||
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
|
||||
expect(UIManager.updateView).toHaveBeenCalledWith(
|
||||
expect.any(Number),
|
||||
'RCTView',
|
||||
{foo: 'baz'},
|
||||
);
|
||||
});
|
||||
|
||||
it('should call UIManager.measure on ref.measure', () => {
|
||||
@@ -234,39 +187,24 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactNative.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
UIManager.measure.mockClear();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.measure.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measure(successCallback);
|
||||
expect(UIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
|
||||
});
|
||||
expect(UIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measure(successCallback);
|
||||
expect(UIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
|
||||
});
|
||||
|
||||
it('should call UIManager.measureInWindow on ref.measureInWindow', () => {
|
||||
@@ -275,39 +213,24 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactNative.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
UIManager.measureInWindow.mockClear();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.measureInWindow.mockClear();
|
||||
|
||||
let viewRef;
|
||||
ReactNative.render(
|
||||
<Component
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measureInWindow(successCallback);
|
||||
expect(UIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
|
||||
});
|
||||
expect(UIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measureInWindow(successCallback);
|
||||
expect(UIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
|
||||
});
|
||||
|
||||
it('should support reactTag in ref.measureLayout', () => {
|
||||
@@ -316,53 +239,38 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
class Subclass extends ReactNative.NativeComponent {
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
UIManager.measureLayout.mockClear();
|
||||
|
||||
const CreateClass = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render() {
|
||||
return <View>{this.props.children}</View>;
|
||||
},
|
||||
});
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactNative.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
|
||||
[View, Subclass, CreateClass].forEach(Component => {
|
||||
UIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactNative.render(
|
||||
<Component>
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<Component
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</Component>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(
|
||||
ReactNative.findNodeHandle(otherRef),
|
||||
successCallback,
|
||||
failureCallback,
|
||||
);
|
||||
expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
expect(UIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(
|
||||
ReactNative.findNodeHandle(otherRef),
|
||||
successCallback,
|
||||
failureCallback,
|
||||
);
|
||||
expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
|
||||
it('should support ref in ref.measureLayout of host components', () => {
|
||||
@@ -371,36 +279,34 @@ describe('ReactNative', () => {
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
[View].forEach(Component => {
|
||||
UIManager.measureLayout.mockClear();
|
||||
UIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactNative.render(
|
||||
<Component>
|
||||
<Component
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</Component>,
|
||||
11,
|
||||
);
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
ReactNative.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
|
||||
expect(UIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
expect(UIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
expect(UIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
|
||||
it('returns the correct instance and calls it in the callback', () => {
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('create-react-class-integration', () => {
|
||||
describe('ReactNative NativeMethodsMixin', () => {
|
||||
let React;
|
||||
let ReactNative;
|
||||
let NativeMethodsMixin;
|
||||
let createReactClass;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
|
||||
createReactClass = require('create-react-class/factory')(
|
||||
React.Component,
|
||||
React.isValidElement,
|
||||
new React.Component().updater,
|
||||
);
|
||||
|
||||
ReactNative = require('react-native-renderer');
|
||||
NativeMethodsMixin =
|
||||
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.NativeMethodsMixin;
|
||||
});
|
||||
|
||||
it('should not warn about default DEV-only legacy lifecycle methods', () => {
|
||||
const View = createReactClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
render: () => null,
|
||||
});
|
||||
|
||||
ReactNative.render(<View />, 1);
|
||||
});
|
||||
|
||||
it('should warn if users specify their own legacy componentWillMount', () => {
|
||||
const View = createReactClass({
|
||||
displayName: 'MyNativeComponent',
|
||||
mixins: [NativeMethodsMixin],
|
||||
componentWillMount: () => {},
|
||||
render: () => null,
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
ReactNative.render(<View />, 1),
|
||||
).toWarnDev('componentWillMount has been renamed', {withoutStack: true});
|
||||
});
|
||||
|
||||
it('should warn if users specify their own legacy componentWillReceiveProps', () => {
|
||||
const View = createReactClass({
|
||||
displayName: 'MyNativeComponent',
|
||||
mixins: [NativeMethodsMixin],
|
||||
componentWillReceiveProps: () => {},
|
||||
render: () => null,
|
||||
});
|
||||
|
||||
expect(() => ReactNative.render(<View />, 1)).toWarnDev(
|
||||
'componentWillReceiveProps has been renamed',
|
||||
{
|
||||
withoutStack: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
} = require('./ReactNative');
|
||||
|
||||
import type {NativeMethodsMixinType} from './ReactNativeTypes';
|
||||
|
||||
const {NativeMethodsMixin} = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||||
|
||||
module.exports = ((NativeMethodsMixin: any): $Exact<NativeMethodsMixinType>);
|
||||
Reference in New Issue
Block a user