/** * 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 from 'TextInputState'; import UIManager from 'UIManager'; import * as ReactNativeAttributePayload 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 = { /** * 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) { 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) { 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, onSuccess: MeasureLayoutOnSuccessCallback, onFail: () => void /* currently unused */, ) { UIManager.measureLayout( findNodeHandle(this), relativeToNativeNode, 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; } const viewConfig: ReactNativeBaseComponentViewConfig<> = maybeInstance.viewConfig; if (__DEV__) { warnForStyleProps(nativeProps, viewConfig.validAttributes); } const updatePayload = ReactNativeAttributePayload.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( maybeInstance._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; }