2016-12-08 10:15:13 +08:00
|
|
|
/**
|
2018-09-08 06:11:23 +08:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2016-12-08 10:15:13 +08:00
|
|
|
*
|
2017-09-25 04:48:13 +08:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-12-08 10:15:13 +08:00
|
|
|
*
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
|
2019-10-31 02:42:37 +08:00
|
|
|
import type {ReactNativeType, HostComponent} from './ReactNativeTypes';
|
2017-10-26 02:07:54 +08:00
|
|
|
import type {ReactNodeList} from 'shared/ReactTypes';
|
|
|
|
|
|
2017-11-03 03:50:03 +08:00
|
|
|
import './ReactNativeInjection';
|
2017-10-26 02:07:54 +08:00
|
|
|
|
2018-11-20 07:32:54 +08:00
|
|
|
import {
|
|
|
|
|
findHostInstance,
|
|
|
|
|
findHostInstanceWithWarning,
|
|
|
|
|
batchedUpdates as batchedUpdatesImpl,
|
2019-05-22 05:03:45 +08:00
|
|
|
batchedEventUpdates,
|
|
|
|
|
discreteUpdates,
|
|
|
|
|
flushDiscreteUpdates,
|
2018-11-20 07:32:54 +08:00
|
|
|
createContainer,
|
|
|
|
|
updateContainer,
|
|
|
|
|
injectIntoDevTools,
|
|
|
|
|
getPublicRootInstance,
|
|
|
|
|
} from 'react-reconciler/inline.native';
|
2018-07-07 08:09:41 +08:00
|
|
|
// TODO: direct imports like some-package/src/* are bad. Fix me.
|
|
|
|
|
import {getStackByFiberInDevAndProd} from 'react-reconciler/src/ReactCurrentFiber';
|
2018-11-20 07:32:54 +08:00
|
|
|
import {createPortal} from 'shared/ReactPortal';
|
|
|
|
|
import {
|
|
|
|
|
setBatchingImplementation,
|
|
|
|
|
batchedUpdates,
|
2019-08-14 22:32:42 +08:00
|
|
|
} from 'legacy-events/ReactGenericBatching';
|
2017-11-03 03:50:03 +08:00
|
|
|
import ReactVersion from 'shared/ReactVersion';
|
2017-10-25 07:55:00 +08:00
|
|
|
// Module provided by RN:
|
2019-05-23 15:23:54 +08:00
|
|
|
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
2017-11-03 03:50:03 +08:00
|
|
|
|
|
|
|
|
import NativeMethodsMixin from './NativeMethodsMixin';
|
|
|
|
|
import ReactNativeComponent from './ReactNativeComponent';
|
2018-11-20 07:32:54 +08:00
|
|
|
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
2017-11-03 03:50:03 +08:00
|
|
|
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
2016-12-08 10:15:13 +08:00
|
|
|
|
Add Batched Mode (#15502)
* Add Batched Mode
React has an unfortunate quirk where updates are sometimes synchronous
-- where React starts rendering immediately within the call stack of
`setState` — and sometimes batched, where updates are flushed at the
end of the current event. Any update that originates within the call
stack of the React event system is batched. This encompasses most
updates, since most updates originate from an event handler like
`onClick` or `onChange`. It also includes updates triggered by lifecycle
methods or effects. But there are also updates that originate outside
React's event system, like timer events, network events, and microtasks
(promise resolution handlers). These are not batched, which results in
both worse performance (multiple render passes instead of single one)
and confusing semantics.
Ideally all updates would be batched by default. Unfortunately, it's
easy for components to accidentally rely on this behavior, so changing
it could break existing apps in subtle ways.
One way to move to a batched-by-default model is to opt into Concurrent
Mode (still experimental). But Concurrent Mode introduces additional
semantic changes that apps may not be ready to adopt.
This commit introduces an additional mode called Batched Mode. Batched
Mode enables a batched-by-default model that defers all updates to the
next React event. Once it begins rendering, React will not yield to
the browser until the entire render is finished.
Batched Mode is superset of Strict Mode. It fires all the same warnings.
It also drops the forked Suspense behavior used by Legacy Mode, in favor
of the proper semantics used by Concurrent Mode.
I have not added any public APIs that expose the new mode yet. I'll do
that in subsequent commits.
* Suspense in Batched Mode
Should have same semantics as Concurrent Mode.
* Use RootTag field to configure type of root
There are three types of roots: Legacy, Batched, and Concurrent.
* flushSync should not flush batched work
Treat Sync and Batched expiration times separately. Only Sync updates
are pushed to our internal queue of synchronous callbacks.
Renamed `flushImmediateQueue` to `flushSyncCallbackQueue` for clarity.
2019-05-14 05:30:39 +08:00
|
|
|
import {LegacyRoot} from 'shared/ReactRootTags';
|
2018-07-13 09:45:37 +08:00
|
|
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
2018-04-10 11:15:10 +08:00
|
|
|
import getComponentName from 'shared/getComponentName';
|
2018-07-17 05:31:59 +08:00
|
|
|
import warningWithoutStack from 'shared/warningWithoutStack';
|
2018-04-10 11:15:10 +08:00
|
|
|
|
2018-07-13 09:45:37 +08:00
|
|
|
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
2018-04-10 11:15:10 +08:00
|
|
|
|
2019-10-31 04:10:16 +08:00
|
|
|
function findHostInstance_DEPRECATED(
|
2019-10-31 02:42:37 +08:00
|
|
|
componentOrHandle: any,
|
|
|
|
|
): ?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,
|
2019-10-31 04:10:16 +08:00
|
|
|
'findHostInstance_DEPRECATED',
|
2019-10-31 02:42:37 +08:00
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
hostInstance = findHostInstance(componentOrHandle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hostInstance == null) {
|
|
|
|
|
return hostInstance;
|
|
|
|
|
}
|
|
|
|
|
if ((hostInstance: any).canonical) {
|
|
|
|
|
// Fabric
|
|
|
|
|
return (hostInstance: any).canonical;
|
|
|
|
|
}
|
|
|
|
|
return hostInstance;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 11:15:10 +08:00
|
|
|
function findNodeHandle(componentOrHandle: any): ?number {
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
const owner = ReactCurrentOwner.current;
|
|
|
|
|
if (owner !== null && owner.stateNode !== null) {
|
2018-07-17 05:31:59 +08:00
|
|
|
warningWithoutStack(
|
2018-04-10 11:15:10 +08:00
|
|
|
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.',
|
2018-07-12 22:32:06 +08:00
|
|
|
getComponentName(owner.type) || 'A component',
|
2018-04-10 11:15:10 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2018-10-13 06:42:00 +08:00
|
|
|
let hostInstance;
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
hostInstance = findHostInstanceWithWarning(
|
|
|
|
|
componentOrHandle,
|
|
|
|
|
'findNodeHandle',
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
hostInstance = findHostInstance(componentOrHandle);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 11:15:10 +08:00
|
|
|
if (hostInstance == null) {
|
|
|
|
|
return hostInstance;
|
|
|
|
|
}
|
2018-05-22 00:54:48 +08:00
|
|
|
if ((hostInstance: any).canonical) {
|
2018-04-10 11:15:10 +08:00
|
|
|
// Fabric
|
2018-05-22 00:54:48 +08:00
|
|
|
return (hostInstance: any).canonical._nativeTag;
|
2018-04-10 11:15:10 +08:00
|
|
|
}
|
|
|
|
|
return hostInstance._nativeTag;
|
|
|
|
|
}
|
2018-01-23 01:58:35 +08:00
|
|
|
|
2018-11-20 07:32:54 +08:00
|
|
|
setBatchingImplementation(
|
|
|
|
|
batchedUpdatesImpl,
|
2019-05-22 05:03:45 +08:00
|
|
|
discreteUpdates,
|
|
|
|
|
flushDiscreteUpdates,
|
|
|
|
|
batchedEventUpdates,
|
2018-08-18 04:17:37 +08:00
|
|
|
);
|
2016-12-08 10:15:13 +08:00
|
|
|
|
2018-04-05 08:18:44 +08:00
|
|
|
function computeComponentStackForErrorReporting(reactTag: number): string {
|
2018-11-20 07:32:54 +08:00
|
|
|
let fiber = getClosestInstanceFromNode(reactTag);
|
2018-04-05 08:18:44 +08:00
|
|
|
if (!fiber) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
2018-07-07 08:09:41 +08:00
|
|
|
return getStackByFiberInDevAndProd(fiber);
|
2018-04-05 08:18:44 +08:00
|
|
|
}
|
|
|
|
|
|
2016-12-08 10:15:13 +08:00
|
|
|
const roots = new Map();
|
|
|
|
|
|
Reorganize code structure (#11288)
* Move files and tests to more meaningful places
* Fix the build
Now that we import reconciler via react-reconciler, I needed to make a few tweaks.
* Update sizes
* Move @preventMunge directive to FB header
* Revert unintentional change
* Fix Flow coverage
I forgot to @flow-ify those files. This uncovered some issues.
* Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a rat in a cage
Pulling minimum wage
Prettier, I love you but you're bringing me down
Prettier, you're safer and you're wasting my time
Our records all show you were filthy but fine
But they shuttered your stores
When you opened the doors
To the cops who were bored once they'd run out of crime
Prettier, you're perfect, oh, please don't change a thing
Your mild billionaire mayor's now convinced he's a king
So the boring collect
I mean all disrespect
In the neighborhood bars I'd once dreamt I would drink
Prettier, I love you but you're freaking me out
There's a ton of the twist but we're fresh out of shout
Like a death in the hall
That you hear through your wall
Prettier, I love you but you're freaking me out
Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a death of the heart
Jesus, where do I start?
But you're still the one pool where I'd happily drown
And oh! Take me off your mailing list
For kids who think it still exists
Yes, for those who think it still exists
Maybe I'm wrong and maybe you're right
Maybe I'm wrong and maybe you're right
Maybe you're right, maybe I'm wrong
And just maybe you're right
And oh! Maybe mother told you true
And there'll always be somebody there for you
And you'll never be alone
But maybe she's wrong and maybe I'm right
And just maybe she's wrong
Maybe she's wrong and maybe I'm right
And if so, here's this song!
2017-10-20 02:50:24 +08:00
|
|
|
const ReactNativeRenderer: ReactNativeType = {
|
2018-04-10 11:15:10 +08:00
|
|
|
NativeComponent: ReactNativeComponent(findNodeHandle, findHostInstance),
|
2017-05-31 12:55:24 +08:00
|
|
|
|
2019-10-31 02:42:37 +08:00
|
|
|
// This is needed for implementation details of TouchableNativeFeedback
|
|
|
|
|
// Remove this once TouchableNativeFeedback doesn't use cloneElement
|
2019-10-31 04:10:16 +08:00
|
|
|
findHostInstance_DEPRECATED,
|
2018-04-10 11:15:10 +08:00
|
|
|
findNodeHandle,
|
2016-12-08 10:15:13 +08:00
|
|
|
|
2019-07-09 04:03:57 +08:00
|
|
|
dispatchCommand(handle: any, command: string, args: Array<any>) {
|
|
|
|
|
if (handle._nativeTag == null) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 09:03:37 +08:00
|
|
|
UIManager.dispatchViewManagerCommand(handle._nativeTag, command, args);
|
2019-07-09 04:03:57 +08:00
|
|
|
},
|
|
|
|
|
|
2017-08-29 05:39:58 +08:00
|
|
|
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
|
2016-12-08 10:15:13 +08:00
|
|
|
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.
|
2019-08-20 04:26:39 +08:00
|
|
|
root = createContainer(containerTag, LegacyRoot, false, null);
|
2016-12-08 10:15:13 +08:00
|
|
|
roots.set(containerTag, root);
|
|
|
|
|
}
|
2018-11-20 07:32:54 +08:00
|
|
|
updateContainer(element, root, null, callback);
|
2016-12-08 10:15:13 +08:00
|
|
|
|
2018-11-20 07:32:54 +08:00
|
|
|
return getPublicRootInstance(root);
|
2016-12-08 10:15:13 +08:00
|
|
|
},
|
|
|
|
|
|
2017-03-14 08:05:18 +08:00
|
|
|
unmountComponentAtNode(containerTag: number) {
|
2016-12-08 10:15:13 +08:00
|
|
|
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?
|
2018-11-20 07:32:54 +08:00
|
|
|
updateContainer(null, root, null, () => {
|
2016-12-17 10:31:39 +08:00
|
|
|
roots.delete(containerTag);
|
|
|
|
|
});
|
2016-12-08 10:15:13 +08:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
unmountComponentAtNodeAndRemoveContainer(containerTag: number) {
|
Reorganize code structure (#11288)
* Move files and tests to more meaningful places
* Fix the build
Now that we import reconciler via react-reconciler, I needed to make a few tweaks.
* Update sizes
* Move @preventMunge directive to FB header
* Revert unintentional change
* Fix Flow coverage
I forgot to @flow-ify those files. This uncovered some issues.
* Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a rat in a cage
Pulling minimum wage
Prettier, I love you but you're bringing me down
Prettier, you're safer and you're wasting my time
Our records all show you were filthy but fine
But they shuttered your stores
When you opened the doors
To the cops who were bored once they'd run out of crime
Prettier, you're perfect, oh, please don't change a thing
Your mild billionaire mayor's now convinced he's a king
So the boring collect
I mean all disrespect
In the neighborhood bars I'd once dreamt I would drink
Prettier, I love you but you're freaking me out
There's a ton of the twist but we're fresh out of shout
Like a death in the hall
That you hear through your wall
Prettier, I love you but you're freaking me out
Prettier, I love you but you're bringing me down
Prettier, I love you but you're bringing me down
Like a death of the heart
Jesus, where do I start?
But you're still the one pool where I'd happily drown
And oh! Take me off your mailing list
For kids who think it still exists
Yes, for those who think it still exists
Maybe I'm wrong and maybe you're right
Maybe I'm wrong and maybe you're right
Maybe you're right, maybe I'm wrong
And just maybe you're right
And oh! Maybe mother told you true
And there'll always be somebody there for you
And you'll never be alone
But maybe she's wrong and maybe I'm right
And just maybe she's wrong
Maybe she's wrong and maybe I'm right
And if so, here's this song!
2017-10-20 02:50:24 +08:00
|
|
|
ReactNativeRenderer.unmountComponentAtNode(containerTag);
|
2016-12-08 10:15:13 +08:00
|
|
|
|
|
|
|
|
// Call back into native to remove all of the subviews from this container
|
|
|
|
|
UIManager.removeRootView(containerTag);
|
|
|
|
|
},
|
|
|
|
|
|
2017-09-12 05:23:54 +08:00
|
|
|
createPortal(
|
2017-03-14 08:05:18 +08:00
|
|
|
children: ReactNodeList,
|
|
|
|
|
containerTag: number,
|
|
|
|
|
key: ?string = null,
|
|
|
|
|
) {
|
2018-11-20 07:32:54 +08:00
|
|
|
return createPortal(children, containerTag, null, key);
|
2016-12-08 10:15:13 +08:00
|
|
|
},
|
|
|
|
|
|
2018-11-20 07:32:54 +08:00
|
|
|
unstable_batchedUpdates: batchedUpdates,
|
2017-05-25 00:06:30 +08:00
|
|
|
|
|
|
|
|
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
|
|
|
|
// Used as a mixin in many createClass-based components
|
2018-04-10 11:15:10 +08:00
|
|
|
NativeMethodsMixin: NativeMethodsMixin(findNodeHandle, findHostInstance),
|
2018-04-05 08:18:44 +08:00
|
|
|
computeComponentStackForErrorReporting,
|
2017-05-25 00:06:30 +08:00
|
|
|
},
|
2016-12-08 10:15:13 +08:00
|
|
|
};
|
|
|
|
|
|
2018-11-20 07:32:54 +08:00
|
|
|
injectIntoDevTools({
|
|
|
|
|
findFiberByHostInstance: getClosestInstanceFromNode,
|
2017-11-03 03:50:03 +08:00
|
|
|
getInspectorDataForViewTag: getInspectorDataForViewTag,
|
2017-08-03 01:56:24 +08:00
|
|
|
bundleType: __DEV__ ? 1 : 0,
|
|
|
|
|
version: ReactVersion,
|
2017-10-19 07:22:21 +08:00
|
|
|
rendererPackageName: 'react-native-renderer',
|
2017-08-03 01:56:24 +08:00
|
|
|
});
|
2017-02-07 01:36:59 +08:00
|
|
|
|
2017-11-03 03:50:03 +08:00
|
|
|
export default ReactNativeRenderer;
|