react/packages/react-native-renderer/src/ReactNativeRenderer.js

245 lines
7.4 KiB
JavaScript

/**
* 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 {ReactNativeType, HostComponent} from './ReactNativeTypes';
import type {ReactNodeList} from 'shared/ReactTypes';
import './ReactNativeInjection';
import {
findHostInstance,
findHostInstanceWithWarning,
batchedUpdates as batchedUpdatesImpl,
batchedEventUpdates,
discreteUpdates,
flushDiscreteUpdates,
createContainer,
updateContainer,
injectIntoDevTools,
getPublicRootInstance,
} from 'react-reconciler/inline.native';
// TODO: direct imports like some-package/src/* are bad. Fix me.
import {getStackByFiberInDevAndProd} from 'react-reconciler/src/ReactCurrentFiber';
import {createPortal} from 'shared/ReactPortal';
import {
setBatchingImplementation,
batchedUpdates,
} from 'legacy-events/ReactGenericBatching';
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';
import {LegacyRoot} from 'shared/ReactRootTags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import getComponentName from 'shared/getComponentName';
import warningWithoutStack from 'shared/warningWithoutStack';
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
function findHostInstance_DEPRECATED(
componentOrHandle: any,
): ?React$ElementRef<HostComponent<mixed>> {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
warningWithoutStack(
owner.stateNode._warnedAboutRefsInRender,
'%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.',
getComponentName(owner.type) || 'A component',
);
owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
if (componentOrHandle._nativeTag) {
return componentOrHandle;
}
if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) {
return componentOrHandle.canonical;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findHostInstance_DEPRECATED',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}
if (hostInstance == null) {
return hostInstance;
}
if ((hostInstance: any).canonical) {
// Fabric
return (hostInstance: any).canonical;
}
return hostInstance;
}
function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
const owner = ReactCurrentOwner.current;
if (owner !== null && owner.stateNode !== null) {
warningWithoutStack(
owner.stateNode._warnedAboutRefsInRender,
'%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.',
getComponentName(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;
}
if ((hostInstance: any).canonical) {
// Fabric
return (hostInstance: any).canonical._nativeTag;
}
return hostInstance._nativeTag;
}
setBatchingImplementation(
batchedUpdatesImpl,
discreteUpdates,
flushDiscreteUpdates,
batchedEventUpdates,
);
function computeComponentStackForErrorReporting(reactTag: number): string {
let fiber = getClosestInstanceFromNode(reactTag);
if (!fiber) {
return '';
}
return getStackByFiberInDevAndProd(fiber);
}
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,
findNodeHandle,
dispatchCommand(handle: any, command: string, args: Array<any>) {
if (handle._nativeTag == null) {
if (__DEV__) {
warningWithoutStack(
handle._nativeTag != null,
"dispatchCommand was called with a ref that isn't a " +
'native component. Use React.forwardRef to get access to the underlying native component',
);
}
return;
}
UIManager.dispatchViewManagerCommand(handle._nativeTag, command, args);
},
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
let root = roots.get(containerTag);
if (!root) {
// TODO (bvaughn): If we decide to keep the wrapper component,
// We could create a wrapper for containerTag as well to reduce special casing.
root = createContainer(containerTag, LegacyRoot, false, null);
roots.set(containerTag, root);
}
updateContainer(element, root, null, callback);
return getPublicRootInstance(root);
},
unmountComponentAtNode(containerTag: number) {
const root = roots.get(containerTag);
if (root) {
// TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
updateContainer(null, root, null, () => {
roots.delete(containerTag);
});
}
},
unmountComponentAtNodeAndRemoveContainer(containerTag: number) {
ReactNativeRenderer.unmountComponentAtNode(containerTag);
// Call back into native to remove all of the subviews from this container
UIManager.removeRootView(containerTag);
},
createPortal(
children: ReactNodeList,
containerTag: number,
key: ?string = null,
) {
return createPortal(children, containerTag, null, key);
},
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,
},
};
injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
getInspectorDataForViewTag: getInspectorDataForViewTag,
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
rendererPackageName: 'react-native-renderer',
});
export default ReactNativeRenderer;