Flat ReactNative renderer bundle [WIP]

Reviewed By: trueadm

Differential Revision: D5013497

fbshipit-source-id: 1e23b08751b8b6e2dd570ff584c815c8a9b8f35f
This commit is contained in:
Brian Vaughn 2017-05-26 10:51:06 -07:00 коммит произвёл Facebook Github Bot
Родитель 21e3d4db20
Коммит 94c565a2c4
173 изменённых файлов: 14373 добавлений и 31629 удалений

Просмотреть файл

@ -18,7 +18,6 @@
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js
[include]

Просмотреть файл

@ -1,5 +0,0 @@
# React Native Renderer
This is a downstream copy of React's renderer code to render into React Native.
The source of truth is the React repo. Please submit any changes upstream to
the [React Core GitHub repository](https://github.com/facebook/react).

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,23 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixin
* @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>);

Просмотреть файл

@ -95,8 +95,8 @@ var addPoolingTo = function<T>(
CopyConstructor: Class<T>,
pooler: Pooler,
): Class<T> & {
getPooled(...args: $ReadOnlyArray<mixed>): /* arguments of the constructor */ T,
release(instance: mixed): void,
getPooled(): /* arguments of the constructor */ T,
release(): void,
} {
// Casting as any so that flow ignores the actual implementation and trusts
// it to match the type we declared

Просмотреть файл

@ -6,12 +6,14 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
* @providesModule ReactPropTypesSecret
* @providesModule ReactDebugTool
*/
'use strict';
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports = ReactPropTypesSecret;
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactDebugTool;

Просмотреть файл

@ -0,0 +1,19 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactGlobalSharedState
*/
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactGlobalSharedState;

Просмотреть файл

@ -13,6 +13,18 @@
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
module.exports = ReactNativeFeatureFlags.useFiber
? require('ReactNativeFiber')
: require('ReactNativeStack');
import type {ReactNativeType} from 'ReactNativeTypes';
let ReactNative;
if (__DEV__) {
ReactNative = ReactNativeFeatureFlags.useFiber
? require('ReactNativeFiber-dev')
: require('ReactNativeStack-dev');
} else {
ReactNative = ReactNativeFeatureFlags.useFiber
? require('ReactNativeFiber-prod')
: require('ReactNativeStack-prod');
}
module.exports = (ReactNative: ReactNativeType);

Просмотреть файл

@ -0,0 +1,20 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentTree
* @flow
*/
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactNativeComponentTree;

Просмотреть файл

@ -0,0 +1,20 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativePropRegistry
* @flow
*/
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactNativePropRegistry;

Просмотреть файл

@ -0,0 +1,89 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTypes
* @flow
*/
'use strict';
import type React from 'react';
export type MeasureOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => void;
export type MeasureInWindowOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
) => void;
export type MeasureLayoutOnSuccessCallback = (
left: number,
top: number,
width: number,
height: number,
) => void;
/**
* This type keeps ReactNativeFiberHostComponent and NativeMethodsMixin in sync.
* It can also provide types for ReactNative applications that use NMM or refs.
*/
export type NativeMethodsMixinType = {
blur(): void,
focus(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void,
): void,
setNativeProps(nativeProps: Object): void,
};
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object,
uiViewClassName: string,
propTypes?: Object,
};
type SecretInternalsType = {
NativeMethodsMixin: NativeMethodsMixinType,
createReactNativeComponentClass(
viewConfig: ReactNativeBaseComponentViewConfig,
): any,
ReactNativeComponentTree: any,
ReactNativePropRegistry: any,
// TODO (bvaughn) Decide which additional types to expose here?
// And how much information to fill in for the above types.
};
/**
* Flat ReactNative renderer bundles are too big for Flow to parse effeciently.
* Provide minimal Flow typing for the high-level RN API and call it a day.
*/
export type ReactNativeType = {
findNodeHandle(componentOrHandle: any): ?number,
render(
element: React.Element<any>,
containerTag: any,
callback: ?Function,
): any,
unmountComponentAtNode(containerTag: number): any,
unmountComponentAtNodeAndRemoveContainer(containerTag: number): any,
unstable_batchedUpdates: any, // TODO (bvaughn) Add types
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: SecretInternalsType,
};

Просмотреть файл

@ -6,16 +6,13 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule getNextDebugID
* @flow
* @providesModule ReactPerf
*/
'use strict';
var nextDebugID = 1;
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
function getNextDebugID(): number {
return nextDebugID++;
}
module.exports = getNextDebugID;
module.exports = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf;

Просмотреть файл

@ -12,9 +12,6 @@
'use strict';
import type {ReactCoroutine, ReactYield} from 'ReactCoroutine';
import type {ReactPortal} from 'ReactPortal';
export type ReactNode =
| ReactElement<any>
| ReactCoroutine
@ -30,3 +27,26 @@ export type ReactNodeList = ReactEmpty | ReactNode;
export type ReactText = string | number;
export type ReactEmpty = null | void | boolean;
export type ReactCoroutine = {
$$typeof: Symbol | number,
key: null | string,
children: any,
// This should be a more specific CoroutineHandler
handler: (props: any, yields: Array<mixed>) => ReactNodeList,
props: any,
};
export type ReactYield = {
$$typeof: Symbol | number,
value: mixed,
};
export type ReactPortal = {
$$typeof: Symbol | number,
key: null | string,
containerInfo: any,
children: ReactNodeList,
// TODO: figure out the API for cross-renderer implementation.
implementation: any,
};

Просмотреть файл

@ -6,9 +6,14 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactVersion
* @providesModule TouchHistoryMath
*/
'use strict';
module.exports = '16.0.0-alpha.12';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.TouchHistoryMath;

Просмотреть файл

@ -0,0 +1,20 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createReactNativeComponentClass
* @flow
*/
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.createReactNativeComponentClass;

Просмотреть файл

@ -6,15 +6,14 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactTypeOfInternalContext
* @flow
* @providesModule takeSnapshot
*/
'use strict';
export type TypeOfInternalContext = number;
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');
module.exports = {
NoContext: 0,
AsyncUpdates: 1,
};
module.exports =
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.takeSnapshot;

Просмотреть файл

@ -1,282 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixin
* @flow
*/
'use strict';
var ReactNative = require('ReactNative');
var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var TextInputState = require('TextInputState');
var UIManager = require('UIManager');
var invariant = require('fbjs/lib/invariant');
var findNodeHandle = require('findNodeHandle');
var {
mountSafeCallback,
throwOnStylesProp,
warnForStyleProps,
} = require('NativeMethodsMixinUtils');
import type {
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
} from 'NativeMethodsMixinUtils';
import type {
ReactNativeBaseComponentViewConfig,
} from 'ReactNativeViewConfigRegistry';
/**
* `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).
*/
// TODO (bvaughn) Figure out how to use the NativeMethodsInterface type to-
// ensure that these mixins and ReactNativeFiberHostComponent stay in sync.
// Unfortunately, using it causes Flow to complain WRT createClass mixins:
// "call of method `createClass`. Expected an exact object instead of ..."
var NativeMethodsMixin = {
/**
* 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(
ReactNative.findNodeHandle(this),
mountSafeCallback(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(
ReactNative.findNodeHandle(this),
mountSafeCallback(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
* `ReactNative.findNodeHandle(component)`.
*/
measureLayout: function(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void /* currently unused */,
) {
UIManager.measureLayout(
ReactNative.findNodeHandle(this),
relativeToNativeNode,
mountSafeCallback(this, onFail),
mountSafeCallback(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) {
// Ensure ReactNative factory function has configured findNodeHandle.
// Requiring it won't execute the factory function until first referenced.
// It's possible for tests that use ReactTestRenderer to reach this point,
// Without having executed ReactNative.
// Defer the factory function until now to avoid a cycle with UIManager.
// TODO (bvaughn) Remove this once ReactNativeStack is dropped.
require('ReactNative');
injectedSetNativeProps(this, nativeProps);
},
/**
* 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(ReactNative.findNodeHandle(this));
},
/**
* Removes focus from an input or view. This is the opposite of `focus()`.
*/
blur: function() {
TextInputState.blurTextInput(ReactNative.findNodeHandle(this));
},
};
// TODO (bvaughn) Inline this once ReactNativeStack is dropped.
function setNativePropsFiber(componentOrHandle: any, 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 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 = findNodeHandle(componentOrHandle);
} 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);
}
var updatePayload = ReactNativeAttributePayload.create(
nativeProps,
viewConfig.validAttributes,
);
UIManager.updateView(
maybeInstance._nativeTag,
viewConfig.uiViewClassName,
updatePayload,
);
}
// TODO (bvaughn) Remove this once ReactNativeStack is dropped.
function setNativePropsStack(componentOrHandle: any, 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 ReactNative.findNodeHandle() because
// We want the instance/wrapper (not the native tag).
let maybeInstance = findNodeHandle(componentOrHandle);
// 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;
}
let viewConfig: ReactNativeBaseComponentViewConfig;
if (maybeInstance.viewConfig !== undefined) {
// ReactNativeBaseComponent
viewConfig = maybeInstance.viewConfig;
} else if (
maybeInstance._instance !== undefined &&
maybeInstance._instance.viewConfig !== undefined
) {
// ReactCompositeComponentWrapper
// Some instances (eg Text) define their own viewConfig
viewConfig = maybeInstance._instance.viewConfig;
} else {
// ReactCompositeComponentWrapper
// Other instances (eg TextInput) defer to their children's viewConfig
while (maybeInstance._renderedComponent !== undefined) {
maybeInstance = maybeInstance._renderedComponent;
}
viewConfig = maybeInstance.viewConfig;
}
const tag: number = typeof maybeInstance.getHostNode === 'function'
? maybeInstance.getHostNode()
: maybeInstance._rootNodeID;
if (__DEV__) {
warnForStyleProps(nativeProps, viewConfig.validAttributes);
}
var updatePayload = ReactNativeAttributePayload.create(
nativeProps,
viewConfig.validAttributes,
);
UIManager.updateView(tag, viewConfig.uiViewClassName, updatePayload);
}
// Switching based on fiber vs stack to avoid a lot of inline checks at runtime.
// HACK Normally this injection would be done by the renderer, but in this case
// that would result in a cycle between ReactNative and NativeMethodsMixin.
// We avoid requiring additional code for this injection so it's probably ok?
// TODO (bvaughn) Remove this once ReactNativeStack is gone.
let injectedSetNativeProps: (
componentOrHandle: any,
nativeProps: Object,
) => void;
if (ReactNativeFeatureFlags.useFiber) {
injectedSetNativeProps = setNativePropsFiber;
} else {
injectedSetNativeProps = setNativePropsStack;
}
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)
var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
invariant(
!NativeMethodsMixin_DEV.componentWillMount &&
!NativeMethodsMixin_DEV.componentWillReceiveProps,
'Do not override existing functions.',
);
NativeMethodsMixin_DEV.componentWillMount = function() {
throwOnStylesProp(this, this.props);
};
NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
throwOnStylesProp(this, newProps);
};
}
module.exports = NativeMethodsMixin;

Просмотреть файл

@ -1,122 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixinUtils
* @flow
*/
'use strict';
export type MeasureOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => void;
export type MeasureInWindowOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
) => void;
export type MeasureLayoutOnSuccessCallback = (
left: number,
top: number,
width: number,
height: number,
) => void;
/**
* Shared between ReactNativeFiberHostComponent and NativeMethodsMixin to keep
* API in sync.
*/
export interface NativeMethodsInterface {
blur(): void,
focus(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void,
): void,
setNativeProps(nativeProps: Object): void,
}
/**
* In the future, we should cleanup callbacks by cancelling them instead of
* using this.
*/
function mountSafeCallback(context: any, callback: ?Function): any {
return function() {
if (!callback) {
return undefined;
}
if (typeof context.__isMounted === 'boolean') {
// TODO(gaearon): this is gross and should be removed.
// It is currently necessary because View uses createClass,
// and so any measure() calls on View (which are done by React
// DevTools) trigger the isMounted() deprecation warning.
if (!context.__isMounted) {
return undefined;
}
// The else branch is important so that we don't
// trigger the deprecation warning by calling isMounted.
} else if (typeof context.isMounted === 'function') {
if (!context.isMounted()) {
return undefined;
}
}
return callback.apply(context, arguments);
};
}
function throwOnStylesProp(component: any, props: any) {
if (props.styles !== undefined) {
var owner = component._owner || null;
var name = component.constructor.displayName;
var msg =
'`styles` is not a supported property of `' +
name +
'`, did ' +
'you mean `style` (singular)?';
if (owner && owner.constructor && owner.constructor.displayName) {
msg +=
'\n\nCheck the `' +
owner.constructor.displayName +
'` parent ' +
' component.';
}
throw new Error(msg);
}
}
function warnForStyleProps(props: any, validAttributes: any) {
for (var key in validAttributes.style) {
if (!(validAttributes[key] || props[key] === undefined)) {
console.error(
'You are setting the style `{ ' +
key +
': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { ' +
key +
': ... } }`',
);
}
}
}
module.exports = {
mountSafeCallback,
throwOnStylesProp,
warnForStyleProps,
};

Просмотреть файл

@ -1,516 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeAttributePayload
* @flow
*/
'use strict';
var ReactNativePropRegistry = require('ReactNativePropRegistry');
var deepDiffer = require('deepDiffer');
var flattenStyle = require('flattenStyle');
var emptyObject = {};
/**
* Create a payload that contains all the updates between two sets of props.
*
* These helpers are all encapsulated into a single module, because they use
* mutation as a performance optimization which leads to subtle shared
* dependencies between the code paths. To avoid this mutable state leaking
* across modules, I've kept them isolated to this module.
*/
type AttributeDiffer = (prevProp: mixed, nextProp: mixed) => boolean;
type AttributePreprocessor = (nextProp: mixed) => mixed;
type CustomAttributeConfiguration =
| {diff: AttributeDiffer, process: AttributePreprocessor}
| {diff: AttributeDiffer}
| {process: AttributePreprocessor};
type AttributeConfiguration = {
[key: string]:
| CustomAttributeConfiguration
| AttributeConfiguration /*| boolean*/,
};
type NestedNode = Array<NestedNode> | Object | number;
// Tracks removed keys
var removedKeys = null;
var removedKeyCount = 0;
function defaultDiffer(prevProp: mixed, nextProp: mixed): boolean {
if (typeof nextProp !== 'object' || nextProp === null) {
// Scalars have already been checked for equality
return true;
} else {
// For objects and arrays, the default diffing algorithm is a deep compare
return deepDiffer(prevProp, nextProp);
}
}
function resolveObject(idOrObject: number | Object): Object {
if (typeof idOrObject === 'number') {
return ReactNativePropRegistry.getByID(idOrObject);
}
return idOrObject;
}
function restoreDeletedValuesInNestedArray(
updatePayload: Object,
node: NestedNode,
validAttributes: AttributeConfiguration,
) {
if (Array.isArray(node)) {
var i = node.length;
while (i-- && removedKeyCount > 0) {
restoreDeletedValuesInNestedArray(
updatePayload,
node[i],
validAttributes,
);
}
} else if (node && removedKeyCount > 0) {
var obj = resolveObject(node);
for (var propKey in removedKeys) {
if (!removedKeys[propKey]) {
continue;
}
var nextProp = obj[propKey];
if (nextProp === undefined) {
continue;
}
var attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
if (typeof nextProp === 'function') {
nextProp = true;
}
if (typeof nextProp === 'undefined') {
nextProp = null;
}
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
updatePayload[propKey] = nextProp;
} else if (
typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function'
) {
// case: CustomAttributeConfiguration
var nextValue = typeof attributeConfig.process === 'function'
? attributeConfig.process(nextProp)
: nextProp;
updatePayload[propKey] = nextValue;
}
removedKeys[propKey] = false;
removedKeyCount--;
}
}
}
function diffNestedArrayProperty(
updatePayload: ?Object,
prevArray: Array<NestedNode>,
nextArray: Array<NestedNode>,
validAttributes: AttributeConfiguration,
): ?Object {
var minLength = prevArray.length < nextArray.length
? prevArray.length
: nextArray.length;
var i;
for (i = 0; i < minLength; i++) {
// Diff any items in the array in the forward direction. Repeated keys
// will be overwritten by later values.
updatePayload = diffNestedProperty(
updatePayload,
prevArray[i],
nextArray[i],
validAttributes,
);
}
for (; i < prevArray.length; i++) {
// Clear out all remaining properties.
updatePayload = clearNestedProperty(
updatePayload,
prevArray[i],
validAttributes,
);
}
for (; i < nextArray.length; i++) {
// Add all remaining properties.
updatePayload = addNestedProperty(
updatePayload,
nextArray[i],
validAttributes,
);
}
return updatePayload;
}
function diffNestedProperty(
updatePayload: ?Object,
prevProp: NestedNode,
nextProp: NestedNode,
validAttributes: AttributeConfiguration,
): ?Object {
if (!updatePayload && prevProp === nextProp) {
// If no properties have been added, then we can bail out quickly on object
// equality.
return updatePayload;
}
if (!prevProp || !nextProp) {
if (nextProp) {
return addNestedProperty(updatePayload, nextProp, validAttributes);
}
if (prevProp) {
return clearNestedProperty(updatePayload, prevProp, validAttributes);
}
return updatePayload;
}
if (!Array.isArray(prevProp) && !Array.isArray(nextProp)) {
// Both are leaves, we can diff the leaves.
return diffProperties(
updatePayload,
resolveObject(prevProp),
resolveObject(nextProp),
validAttributes,
);
}
if (Array.isArray(prevProp) && Array.isArray(nextProp)) {
// Both are arrays, we can diff the arrays.
return diffNestedArrayProperty(
updatePayload,
prevProp,
nextProp,
validAttributes,
);
}
if (Array.isArray(prevProp)) {
return diffProperties(
updatePayload,
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(prevProp),
// $FlowFixMe - We know that this isn't an array because of above flow.
resolveObject(nextProp),
validAttributes,
);
}
return diffProperties(
updatePayload,
resolveObject(prevProp),
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(nextProp),
validAttributes,
);
}
/**
* addNestedProperty takes a single set of props and valid attribute
* attribute configurations. It processes each prop and adds it to the
* updatePayload.
*/
function addNestedProperty(
updatePayload: ?Object,
nextProp: NestedNode,
validAttributes: AttributeConfiguration,
) {
if (!nextProp) {
return updatePayload;
}
if (!Array.isArray(nextProp)) {
// Add each property of the leaf.
return addProperties(
updatePayload,
resolveObject(nextProp),
validAttributes,
);
}
for (var i = 0; i < nextProp.length; i++) {
// Add all the properties of the array.
updatePayload = addNestedProperty(
updatePayload,
nextProp[i],
validAttributes,
);
}
return updatePayload;
}
/**
* clearNestedProperty takes a single set of props and valid attributes. It
* adds a null sentinel to the updatePayload, for each prop key.
*/
function clearNestedProperty(
updatePayload: ?Object,
prevProp: NestedNode,
validAttributes: AttributeConfiguration,
): ?Object {
if (!prevProp) {
return updatePayload;
}
if (!Array.isArray(prevProp)) {
// Add each property of the leaf.
return clearProperties(
updatePayload,
resolveObject(prevProp),
validAttributes,
);
}
for (var i = 0; i < prevProp.length; i++) {
// Add all the properties of the array.
updatePayload = clearNestedProperty(
updatePayload,
prevProp[i],
validAttributes,
);
}
return updatePayload;
}
/**
* diffProperties takes two sets of props and a set of valid attributes
* and write to updatePayload the values that changed or were deleted.
* If no updatePayload is provided, a new one is created and returned if
* anything changed.
*/
function diffProperties(
updatePayload: ?Object,
prevProps: Object,
nextProps: Object,
validAttributes: AttributeConfiguration,
): ?Object {
var attributeConfig: ?(CustomAttributeConfiguration | AttributeConfiguration);
var nextProp;
var prevProp;
for (var propKey in nextProps) {
attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
prevProp = prevProps[propKey];
nextProp = nextProps[propKey];
// functions are converted to booleans as markers that the associated
// events should be sent from native.
if (typeof nextProp === 'function') {
nextProp = (true: any);
// If nextProp is not a function, then don't bother changing prevProp
// since nextProp will win and go into the updatePayload regardless.
if (typeof prevProp === 'function') {
prevProp = (true: any);
}
}
// An explicit value of undefined is treated as a null because it overrides
// any other preceding value.
if (typeof nextProp === 'undefined') {
nextProp = (null: any);
if (typeof prevProp === 'undefined') {
prevProp = (null: any);
}
}
if (removedKeys) {
removedKeys[propKey] = false;
}
if (updatePayload && updatePayload[propKey] !== undefined) {
// Something else already triggered an update to this key because another
// value diffed. Since we're now later in the nested arrays our value is
// more important so we need to calculate it and override the existing
// value. It doesn't matter if nothing changed, we'll set it anyway.
// Pattern match on: attributeConfig
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
updatePayload[propKey] = nextProp;
} else if (
typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function'
) {
// case: CustomAttributeConfiguration
var nextValue = typeof attributeConfig.process === 'function'
? attributeConfig.process(nextProp)
: nextProp;
updatePayload[propKey] = nextValue;
}
continue;
}
if (prevProp === nextProp) {
continue; // nothing changed
}
// Pattern match on: attributeConfig
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
if (defaultDiffer(prevProp, nextProp)) {
// a normal leaf has changed
(updatePayload || (updatePayload = {}))[propKey] = nextProp;
}
} else if (
typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function'
) {
// case: CustomAttributeConfiguration
var shouldUpdate =
prevProp === undefined ||
(typeof attributeConfig.diff === 'function'
? attributeConfig.diff(prevProp, nextProp)
: defaultDiffer(prevProp, nextProp));
if (shouldUpdate) {
nextValue = typeof attributeConfig.process === 'function'
? attributeConfig.process(nextProp)
: nextProp;
(updatePayload || (updatePayload = {}))[propKey] = nextValue;
}
} else {
// default: fallthrough case when nested properties are defined
removedKeys = null;
removedKeyCount = 0;
// We think that attributeConfig is not CustomAttributeConfiguration at
// this point so we assume it must be AttributeConfiguration.
updatePayload = diffNestedProperty(
updatePayload,
prevProp,
nextProp,
((attributeConfig: any): AttributeConfiguration),
);
if (removedKeyCount > 0 && updatePayload) {
restoreDeletedValuesInNestedArray(
updatePayload,
nextProp,
((attributeConfig: any): AttributeConfiguration),
);
removedKeys = null;
}
}
}
// Also iterate through all the previous props to catch any that have been
// removed and make sure native gets the signal so it can reset them to the
// default.
for (propKey in prevProps) {
if (nextProps[propKey] !== undefined) {
continue; // we've already covered this key in the previous pass
}
attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
if (updatePayload && updatePayload[propKey] !== undefined) {
// This was already updated to a diff result earlier.
continue;
}
prevProp = prevProps[propKey];
if (prevProp === undefined) {
continue; // was already empty anyway
}
// Pattern match on: attributeConfig
if (
typeof attributeConfig !== 'object' ||
typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function'
) {
// case: CustomAttributeConfiguration | !Object
// Flag the leaf property for removal by sending a sentinel.
(updatePayload || (updatePayload = {}))[propKey] = null;
if (!removedKeys) {
removedKeys = {};
}
if (!removedKeys[propKey]) {
removedKeys[propKey] = true;
removedKeyCount++;
}
} else {
// default:
// This is a nested attribute configuration where all the properties
// were removed so we need to go through and clear out all of them.
updatePayload = clearNestedProperty(
updatePayload,
prevProp,
((attributeConfig: any): AttributeConfiguration),
);
}
}
return updatePayload;
}
/**
* addProperties adds all the valid props to the payload after being processed.
*/
function addProperties(
updatePayload: ?Object,
props: Object,
validAttributes: AttributeConfiguration,
): ?Object {
// TODO: Fast path
return diffProperties(updatePayload, emptyObject, props, validAttributes);
}
/**
* clearProperties clears all the previous props by adding a null sentinel
* to the payload for each valid key.
*/
function clearProperties(
updatePayload: ?Object,
prevProps: Object,
validAttributes: AttributeConfiguration,
): ?Object {
// TODO: Fast path
return diffProperties(updatePayload, prevProps, emptyObject, validAttributes);
}
var ReactNativeAttributePayload = {
create: function(
props: Object,
validAttributes: AttributeConfiguration,
): ?Object {
return addProperties(
null, // updatePayload
props,
validAttributes,
);
},
diff: function(
prevProps: Object,
nextProps: Object,
validAttributes: AttributeConfiguration,
): ?Object {
return diffProperties(
null, // updatePayload
prevProps,
nextProps,
validAttributes,
);
},
};
module.exports = ReactNativeAttributePayload;

Просмотреть файл

@ -1,194 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeBaseComponent
* @flow
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChild = require('ReactMultiChild');
var UIManager = require('UIManager');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object,
uiViewClassName: string,
};
// require('UIManagerStatTracker').install(); // uncomment to enable
/**
* @constructor ReactNativeBaseComponent
* @extends ReactComponent
* @extends ReactMultiChild
* @param {!object} UIKit View Configuration.
*/
var ReactNativeBaseComponent = function(
viewConfig: ReactNativeBaseComponentViewConfig,
) {
this.viewConfig = viewConfig;
};
/**
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
* which is a `viewID` ... see the return value for `mountComponent` !
*/
ReactNativeBaseComponent.Mixin = {
getPublicInstance: function() {
// TODO: This should probably use a composite wrapper
return this;
},
unmountComponent: function(safely, skipLifecycle) {
ReactNativeComponentTree.uncacheNode(this);
this.unmountChildren(safely, skipLifecycle);
this._rootNodeID = 0;
},
/**
* Every native component is responsible for allocating its own `tag`, and
* issuing the native `createView` command. But it is not responsible for
* recording the fact that its own `rootNodeID` is associated with a
* `nodeHandle`. Only the code that actually adds its `nodeHandle` (`tag`) as
* a child of a container can confidently record that in
* `ReactNativeTagHandles`.
*/
initializeChildren: function(children, containerTag, transaction, context) {
var mountImages = this.mountChildren(children, transaction, context);
// In a well balanced tree, half of the nodes are in the bottom row and have
// no children - let's avoid calling out to the native bridge for a large
// portion of the children.
if (mountImages.length) {
// TODO: Pool these per platform view class. Reusing the `mountImages`
// array would likely be a jit deopt.
var createdTags = [];
for (var i = 0, l = mountImages.length; i < l; i++) {
var mountImage = mountImages[i];
var childTag = mountImage;
createdTags[i] = childTag;
}
UIManager.setChildren(containerTag, createdTags);
}
},
/**
* Updates the component's currently mounted representation.
*
* @param {object} nextElement
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @internal
*/
receiveComponent: function(nextElement, transaction, context) {
var prevElement = this._currentElement;
this._currentElement = nextElement;
if (__DEV__) {
for (var key in this.viewConfig.validAttributes) {
if (nextElement.props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(nextElement.props[key]);
}
}
}
var updatePayload = ReactNativeAttributePayload.diff(
prevElement.props,
nextElement.props,
this.viewConfig.validAttributes,
);
if (updatePayload) {
UIManager.updateView(
this._rootNodeID,
this.viewConfig.uiViewClassName,
updatePayload,
);
}
this.updateChildren(nextElement.props.children, transaction, context);
},
/**
* Currently this still uses IDs for reconciliation so this can return null.
*
* @return {null} Null.
*/
getHostNode: function() {
return this._rootNodeID;
},
/**
* @param {ReactNativeReconcileTransaction} transaction
* @param {?ReactNativeBaseComponent} the parent component instance
* @param {?object} info about the host container
* @param {object} context
* @return {string} Unique iOS view tag.
*/
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
) {
var tag = ReactNativeTagHandles.allocateTag();
this._rootNodeID = tag;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
if (__DEV__) {
for (var key in this.viewConfig.validAttributes) {
if (this._currentElement.props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(this._currentElement.props[key]);
}
}
}
var updatePayload = ReactNativeAttributePayload.create(
this._currentElement.props,
this.viewConfig.validAttributes,
);
var nativeTopRootTag = hostContainerInfo._tag;
UIManager.createView(
tag,
this.viewConfig.uiViewClassName,
nativeTopRootTag,
updatePayload,
);
ReactNativeComponentTree.precacheNode(this, tag);
this.initializeChildren(
this._currentElement.props.children,
tag,
transaction,
context,
);
return tag;
},
};
/**
* Order of mixins is important. ReactNativeBaseComponent overrides methods in
* ReactMultiChild.
*/
Object.assign(
ReactNativeBaseComponent.prototype,
ReactMultiChild,
ReactNativeBaseComponent.Mixin,
NativeMethodsMixin,
);
module.exports = ReactNativeBaseComponent;

Просмотреть файл

@ -1,70 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeBridgeEventPlugin
* @flow
*/
'use strict';
var EventPropagators = require('EventPropagators');
var SyntheticEvent = require('SyntheticEvent');
var UIManager = require('UIManager');
var warning = require('fbjs/lib/warning');
var customBubblingEventTypes = UIManager.customBubblingEventTypes;
var customDirectEventTypes = UIManager.customDirectEventTypes;
var allTypesByEventName = {};
for (var bubblingTypeName in customBubblingEventTypes) {
allTypesByEventName[bubblingTypeName] =
customBubblingEventTypes[bubblingTypeName];
}
for (var directTypeName in customDirectEventTypes) {
warning(
!customBubblingEventTypes[directTypeName],
'Event cannot be both direct and bubbling: %s',
directTypeName,
);
allTypesByEventName[directTypeName] = customDirectEventTypes[directTypeName];
}
var ReactNativeBridgeEventPlugin = {
eventTypes: {...customBubblingEventTypes, ...customDirectEventTypes},
/**
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType: string,
targetInst: Object,
nativeEvent: Event,
nativeEventTarget: Object,
): ?Object {
var bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
var directDispatchConfig = customDirectEventTypes[topLevelType];
var event = SyntheticEvent.getPooled(
bubbleDispatchConfig || directDispatchConfig,
targetInst,
nativeEvent,
nativeEventTarget,
);
if (bubbleDispatchConfig) {
EventPropagators.accumulateTwoPhaseDispatches(event);
} else if (directDispatchConfig) {
EventPropagators.accumulateDirectDispatches(event);
} else {
return null;
}
return event;
},
};
module.exports = ReactNativeBridgeEventPlugin;

Просмотреть файл

@ -1,30 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentEnvironment
* @flow
*/
'use strict';
var ReactNativeDOMIDOperations = require('ReactNativeDOMIDOperations');
var ReactNativeReconcileTransaction = require('ReactNativeReconcileTransaction');
var ReactNativeComponentEnvironment = {
processChildrenUpdates: ReactNativeDOMIDOperations.dangerouslyProcessChildrenUpdates,
replaceNodeWithMarkup: ReactNativeDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
/**
* @param {DOMElement} Element to clear.
*/
clearNode: function(/*containerView*/) {},
ReactReconcileTransaction: ReactNativeReconcileTransaction,
};
module.exports = ReactNativeComponentEnvironment;

Просмотреть файл

@ -1,92 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentTree
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var instanceCache = {};
var instanceProps = {};
/**
* Drill down (through composites and empty components) until we get a host or
* host text component.
*
* This is pretty polymorphic but unavoidable with the current structure we have
* for `_renderedChildren`.
*/
function getRenderedHostOrTextFromComponent(component) {
var rendered;
while ((rendered = component._renderedComponent)) {
component = rendered;
}
return component;
}
/**
* Populate `_hostNode` on the rendered host/text component with the given
* DOM node. The passed `inst` can be a composite.
*/
function precacheNode(inst, tag) {
var nativeInst = getRenderedHostOrTextFromComponent(inst);
instanceCache[tag] = nativeInst;
}
function precacheFiberNode(hostInst, tag) {
instanceCache[tag] = hostInst;
}
function uncacheNode(inst) {
var tag = inst._rootNodeID;
if (tag) {
delete instanceCache[tag];
}
}
function uncacheFiberNode(tag) {
delete instanceCache[tag];
delete instanceProps[tag];
}
function getInstanceFromTag(tag) {
return instanceCache[tag] || null;
}
function getTagFromInstance(inst) {
// TODO (bvaughn) Clean up once Stack is deprecated
var tag = typeof inst.tag !== 'number'
? inst._rootNodeID
: inst.stateNode._nativeTag;
invariant(tag, 'All native instances should have a tag.');
return tag;
}
function getFiberCurrentPropsFromNode(stateNode) {
return instanceProps[stateNode._nativeTag] || null;
}
function updateFiberProps(tag, props) {
instanceProps[tag] = props;
}
var ReactNativeComponentTree = {
getClosestInstanceFromNode: getInstanceFromTag,
getInstanceFromNode: getInstanceFromTag,
getNodeFromInstance: getTagFromInstance,
precacheFiberNode,
precacheNode,
uncacheFiberNode,
uncacheNode,
getFiberCurrentPropsFromNode,
updateFiberProps,
};
module.exports = ReactNativeComponentTree;

Просмотреть файл

@ -1,21 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeContainerInfo
* @flow
*/
'use strict';
function ReactNativeContainerInfo(tag: number) {
var info = {
_tag: tag,
};
return info;
}
module.exports = ReactNativeContainerInfo;

Просмотреть файл

@ -1,85 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeDOMIDOperations
*/
'use strict';
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var UIManager = require('UIManager');
/**
* Updates a component's children by processing a series of updates.
* For each of the update/create commands, the `fromIndex` refers to the index
* that the item existed at *before* any of the updates are applied, and the
* `toIndex` refers to the index after *all* of the updates are applied
* (including deletes/moves). TODO: refactor so this can be shared with
* DOMChildrenOperations.
*
* @param {ReactNativeBaseComponent} updates List of update configurations.
* @param {array<string>} markup List of markup strings - in the case of React
* IOS, the ids of new components assumed to be already created.
*/
var dangerouslyProcessChildrenUpdates = function(inst, childrenUpdates) {
if (!childrenUpdates.length) {
return;
}
var containerTag = ReactNativeComponentTree.getNodeFromInstance(inst);
var moveFromIndices;
var moveToIndices;
var addChildTags;
var addAtIndices;
var removeAtIndices;
for (var i = 0; i < childrenUpdates.length; i++) {
var update = childrenUpdates[i];
if (update.type === 'MOVE_EXISTING') {
(moveFromIndices || (moveFromIndices = [])).push(update.fromIndex);
(moveToIndices || (moveToIndices = [])).push(update.toIndex);
} else if (update.type === 'REMOVE_NODE') {
(removeAtIndices || (removeAtIndices = [])).push(update.fromIndex);
} else if (update.type === 'INSERT_MARKUP') {
var mountImage = update.content;
var tag = mountImage;
(addAtIndices || (addAtIndices = [])).push(update.toIndex);
(addChildTags || (addChildTags = [])).push(tag);
}
}
UIManager.manageChildren(
containerTag,
moveFromIndices,
moveToIndices,
addChildTags,
addAtIndices,
removeAtIndices,
);
};
/**
* Operations used to process updates to DOM nodes. This is made injectable via
* `ReactComponent.DOMIDOperations`.
*/
var ReactNativeDOMIDOperations = {
dangerouslyProcessChildrenUpdates,
/**
* Replaces a view that exists in the document with markup.
*
* @param {string} id ID of child to be replaced.
* @param {string} markup Mount image to replace child with id.
*/
dangerouslyReplaceNodeWithMarkupByID: function(id, mountImage) {
var oldTag = id;
UIManager.replaceExistingNonRootView(oldTag, mountImage);
},
};
module.exports = ReactNativeDOMIDOperations;

Просмотреть файл

@ -1,201 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeEventEmitter
* @flow
*/
'use strict';
var EventPluginHub = require('EventPluginHub');
var EventPluginRegistry = require('EventPluginRegistry');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactGenericBatching = require('ReactGenericBatching');
var warning = require('fbjs/lib/warning');
/**
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
* serialized worker boundary.
*/
// Shared default empty native event - conserve memory.
var EMPTY_NATIVE_EVENT = {};
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
var touchSubsequence = function(touches, indices) {
var ret = [];
for (var i = 0; i < indices.length; i++) {
ret.push(touches[indices[i]]);
}
return ret;
};
/**
* TODO: Pool all of this.
*
* Destroys `touches` by removing touch objects at indices `indices`. This is
* to maintain compatibility with W3C touch "end" events, where the active
* touches don't include the set that has just been "ended".
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
var removeTouchesAtIndices = function(
touches: Array<Object>,
indices: Array<number>,
): Array<Object> {
var rippedOut = [];
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
var temp: Array<?Object> = (touches: Array<any>);
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
rippedOut.push(touches[index]);
temp[index] = null;
}
var fillAt = 0;
for (var j = 0; j < temp.length; j++) {
var cur = temp[j];
if (cur !== null) {
temp[fillAt++] = cur;
}
}
temp.length = fillAt;
return rippedOut;
};
var ReactNativeEventEmitter = {
...ReactEventEmitterMixin,
registrationNames: EventPluginRegistry.registrationNameModules,
getListener: EventPluginHub.getListener,
/**
* Internal version of `receiveEvent` in terms of normalized (non-tag)
* `rootNodeID`.
*
* @see receiveEvent.
*
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
_receiveRootNodeIDEvent: function(
rootNodeID: number,
topLevelType: string,
nativeEventParam: Object,
) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
var inst = ReactNativeComponentTree.getInstanceFromNode(rootNodeID);
ReactGenericBatching.batchedUpdates(function() {
ReactNativeEventEmitter.handleTopLevel(
topLevelType,
inst,
nativeEvent,
nativeEvent.target,
);
});
// React Native doesn't use ReactControlledComponent but if it did, here's
// where it would do it.
},
/**
* Publicly exposed method on module for native objc to invoke when a top
* level event is extracted.
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
receiveEvent: function(
tag: number,
topLevelType: string,
nativeEventParam: Object,
) {
var rootNodeID = tag;
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
topLevelType,
nativeEventParam,
);
},
/**
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
* efficient representation of `Touch` objects, and other information that
* can be used to construct W3C compliant `Event` and `Touch` lists.
*
* This may create dispatch behavior that differs than web touch handling. We
* loop through each of the changed touches and receive it as a single event.
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
* two separate touch event dispatches - when they arguably should be one.
*
* This implementation reuses the `Touch` objects themselves as the `Event`s
* since we dispatch an event for each touch (though that might not be spec
* compliant). The main purpose of reusing them is to save allocations.
*
* TODO: Dispatch multiple changed touches in one event. The bubble path
* could be the first common ancestor of all the `changedTouches`.
*
* One difference between this behavior and W3C spec: cancelled touches will
* not appear in `.touches`, or in any future `.touches`, though they may
* still be "actively touching the surface".
*
* Web desktop polyfills only need to construct a fake touch event with
* identifier 0, also abandoning traditional click handlers.
*/
receiveTouches: function(
eventTopLevelType: string,
touches: Array<Object>,
changedIndices: Array<number>,
) {
var changedTouches = eventTopLevelType === 'topTouchEnd' ||
eventTopLevelType === 'topTouchCancel'
? removeTouchesAtIndices(touches, changedIndices)
: touchSubsequence(touches, changedIndices);
for (var jj = 0; jj < changedTouches.length; jj++) {
var touch = changedTouches[jj];
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.changedTouches = changedTouches;
touch.touches = touches;
var nativeEvent = touch;
var rootNodeID = null;
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactNativeTagHandles.tagsStartAt) {
if (__DEV__) {
warning(
false,
'A view is reporting that a touch occurred on tag zero.',
);
}
} else {
rootNodeID = target;
}
}
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
eventTopLevelType,
nativeEvent,
);
}
},
};
module.exports = ReactNativeEventEmitter;

Просмотреть файл

@ -1,19 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeEventPluginOrder
* @flow
*/
'use strict';
var ReactNativeEventPluginOrder = [
'ResponderEventPlugin',
'ReactNativeBridgeEventPlugin',
];
module.exports = ReactNativeEventPluginOrder;

Просмотреть файл

@ -1,474 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeFiber
* @flow
*/
'use strict';
const ReactFiberErrorLogger = require('ReactFiberErrorLogger');
const ReactFiberReconciler = require('ReactFiberReconciler');
const ReactGenericBatching = require('ReactGenericBatching');
const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
const ReactNativeComponentTree = require('ReactNativeComponentTree');
const ReactNativeFiberErrorDialog = require('ReactNativeFiberErrorDialog');
const ReactNativeFiberHostComponent = require('ReactNativeFiberHostComponent');
const ReactNativeInjection = require('ReactNativeInjection');
const ReactNativeTagHandles = require('ReactNativeTagHandles');
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
const ReactPortal = require('ReactPortal');
const ReactVersion = require('ReactVersion');
const UIManager = require('UIManager');
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
const emptyObject = require('fbjs/lib/emptyObject');
const findNodeHandle = require('findNodeHandle');
const invariant = require('fbjs/lib/invariant');
const {injectInternals} = require('ReactFiberDevToolsHook');
import type {Element} from 'React';
import type {Fiber} from 'ReactFiber';
import type {
ReactNativeBaseComponentViewConfig,
} from 'ReactNativeViewConfigRegistry';
import type {ReactNodeList} from 'ReactTypes';
const {
precacheFiberNode,
uncacheFiberNode,
updateFiberProps,
} = ReactNativeComponentTree;
ReactNativeInjection.inject();
type Container = number;
export type Instance = {
_children: Array<Instance | number>,
_nativeTag: number,
viewConfig: ReactNativeBaseComponentViewConfig,
};
type Props = Object;
type TextInstance = number;
function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
if (typeof node === 'number') {
// Leaf node (eg text)
uncacheFiberNode(node);
} else {
uncacheFiberNode((node: any)._nativeTag);
(node: any)._children.forEach(recursivelyUncacheFiberNode);
}
}
const NativeRenderer = ReactFiberReconciler({
appendChild(
parentInstance: Instance | Container,
child: Instance | TextInstance,
): void {
const childTag = typeof child === 'number' ? child : child._nativeTag;
if (typeof parentInstance === 'number') {
// Root container
UIManager.setChildren(
parentInstance, // containerTag
[childTag], // reactTags
);
} else {
const children = parentInstance._children;
const index = children.indexOf(child);
if (index >= 0) {
children.splice(index, 1);
children.push(child);
UIManager.manageChildren(
parentInstance._nativeTag, // containerTag
[index], // moveFromIndices
[children.length - 1], // moveToIndices
[], // addChildReactTags
[], // addAtIndices
[], // removeAtIndices
);
} else {
children.push(child);
UIManager.manageChildren(
parentInstance._nativeTag, // containerTag
[], // moveFromIndices
[], // moveToIndices
[childTag], // addChildReactTags
[children.length - 1], // addAtIndices
[], // removeAtIndices
);
}
}
},
appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
parentInstance._children.push(child);
},
commitTextUpdate(
textInstance: TextInstance,
oldText: string,
newText: string,
): void {
UIManager.updateView(
textInstance, // reactTag
'RCTRawText', // viewName
{text: newText}, // props
);
},
commitMount(
instance: Instance,
type: string,
newProps: Props,
internalInstanceHandle: Object,
): void {
// Noop
},
commitUpdate(
instance: Instance,
updatePayloadTODO: Object,
type: string,
oldProps: Props,
newProps: Props,
internalInstanceHandle: Object,
): void {
const viewConfig = instance.viewConfig;
updateFiberProps(instance._nativeTag, newProps);
const updatePayload = ReactNativeAttributePayload.diff(
oldProps,
newProps,
viewConfig.validAttributes,
);
UIManager.updateView(
instance._nativeTag, // reactTag
viewConfig.uiViewClassName, // viewName
updatePayload, // props
);
},
createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: {},
internalInstanceHandle: Object,
): Instance {
const tag = ReactNativeTagHandles.allocateTag();
const viewConfig = ReactNativeViewConfigRegistry.get(type);
if (__DEV__) {
for (const key in viewConfig.validAttributes) {
if (props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(props[key]);
}
}
}
const updatePayload = ReactNativeAttributePayload.create(
props,
viewConfig.validAttributes,
);
UIManager.createView(
tag, // reactTag
viewConfig.uiViewClassName, // viewName
rootContainerInstance, // rootTag
updatePayload, // props
);
const component = new ReactNativeFiberHostComponent(tag, viewConfig);
precacheFiberNode(internalInstanceHandle, tag);
updateFiberProps(tag, props);
// Not sure how to avoid this cast. Flow is okay if the component is defined
// in the same file but if it's external it can't see the types.
return ((component: any): Instance);
},
createTextInstance(
text: string,
rootContainerInstance: Container,
hostContext: {},
internalInstanceHandle: Object,
): TextInstance {
const tag = ReactNativeTagHandles.allocateTag();
UIManager.createView(
tag, // reactTag
'RCTRawText', // viewName
rootContainerInstance, // rootTag
{text: text}, // props
);
precacheFiberNode(internalInstanceHandle, tag);
return tag;
},
finalizeInitialChildren(
parentInstance: Instance,
type: string,
props: Props,
rootContainerInstance: Container,
): boolean {
// Don't send a no-op message over the bridge.
if (parentInstance._children.length === 0) {
return false;
}
// Map from child objects to native tags.
// Either way we need to pass a copy of the Array to prevent it from being frozen.
const nativeTags = parentInstance._children.map(
child =>
(typeof child === 'number'
? child // Leaf node (eg text)
: child._nativeTag),
);
UIManager.setChildren(
parentInstance._nativeTag, // containerTag
nativeTags, // reactTags
);
return false;
},
getRootHostContext(): {} {
return emptyObject;
},
getChildHostContext(): {} {
return emptyObject;
},
getPublicInstance(instance) {
return instance;
},
insertBefore(
parentInstance: Instance | Container,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance,
): void {
// TODO (bvaughn): Remove this check when...
// We create a wrapper object for the container in ReactNative render()
// Or we refactor to remove wrapper objects entirely.
// For more info on pros/cons see PR #8560 description.
invariant(
typeof parentInstance !== 'number',
'Container does not support insertBefore operation',
);
const children = (parentInstance: any)._children;
const index = children.indexOf(child);
// Move existing child or add new child?
if (index >= 0) {
children.splice(index, 1);
const beforeChildIndex = children.indexOf(beforeChild);
children.splice(beforeChildIndex, 0, child);
UIManager.manageChildren(
(parentInstance: any)._nativeTag, // containerID
[index], // moveFromIndices
[beforeChildIndex], // moveToIndices
[], // addChildReactTags
[], // addAtIndices
[], // removeAtIndices
);
} else {
const beforeChildIndex = children.indexOf(beforeChild);
children.splice(beforeChildIndex, 0, child);
const childTag = typeof child === 'number' ? child : child._nativeTag;
UIManager.manageChildren(
(parentInstance: any)._nativeTag, // containerID
[], // moveFromIndices
[], // moveToIndices
[childTag], // addChildReactTags
[beforeChildIndex], // addAtIndices
[], // removeAtIndices
);
}
},
prepareForCommit(): void {
// Noop
},
prepareUpdate(
instance: Instance,
type: string,
oldProps: Props,
newProps: Props,
rootContainerInstance: Container,
hostContext: {},
): null | Object {
return emptyObject;
},
removeChild(
parentInstance: Instance | Container,
child: Instance | TextInstance,
): void {
recursivelyUncacheFiberNode(child);
if (typeof parentInstance === 'number') {
UIManager.manageChildren(
parentInstance, // containerID
[], // moveFromIndices
[], // moveToIndices
[], // addChildReactTags
[], // addAtIndices
[0], // removeAtIndices
);
} else {
const children = parentInstance._children;
const index = children.indexOf(child);
children.splice(index, 1);
UIManager.manageChildren(
parentInstance._nativeTag, // containerID
[], // moveFromIndices
[], // moveToIndices
[], // addChildReactTags
[], // addAtIndices
[index], // removeAtIndices
);
}
},
resetAfterCommit(): void {
// Noop
},
resetTextContent(instance: Instance): void {
// Noop
},
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
scheduleAnimationCallback: global.requestAnimationFrame,
scheduleDeferredCallback: global.requestIdleCallback,
shouldSetTextContent(props: Props): boolean {
// TODO (bvaughn) Revisit this decision.
// Always returning false simplifies the createInstance() implementation,
// But creates an additional child Fiber for raw text children.
// No additional native views are created though.
// It's not clear to me which is better so I'm deferring for now.
// More context @ github.com/facebook/react/pull/8560#discussion_r92111303
return false;
},
useSyncScheduling: true,
});
ReactGenericBatching.injection.injectFiberBatchedUpdates(
NativeRenderer.batchedUpdates,
);
const roots = new Map();
findNodeHandle.injection.injectFindNode((fiber: Fiber) =>
NativeRenderer.findHostInstance(fiber),
);
findNodeHandle.injection.injectFindRootNodeID(instance => instance);
// Intercept lifecycle errors and ensure they are shown with the correct stack
// trace within the native redbox component.
ReactFiberErrorLogger.injection.injectDialog(
ReactNativeFiberErrorDialog.showDialog,
);
const ReactNative = {
// External users of findNodeHandle() expect the host tag number return type.
// The injected findNodeHandle() strategy returns the instance wrapper though.
// See NativeMethodsMixin#setNativeProps for more info on why this is done.
findNodeHandle(componentOrHandle: any): ?number {
const instance: any = findNodeHandle(componentOrHandle);
if (instance == null || typeof instance === 'number') {
return instance;
}
return instance._nativeTag;
},
render(element: 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 = NativeRenderer.createContainer(containerTag);
roots.set(containerTag, root);
}
NativeRenderer.updateContainer(element, root, null, callback);
return NativeRenderer.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?
NativeRenderer.updateContainer(null, root, null, () => {
roots.delete(containerTag);
});
}
},
unmountComponentAtNodeAndRemoveContainer(containerTag: number) {
ReactNative.unmountComponentAtNode(containerTag);
// Call back into native to remove all of the subviews from this container
UIManager.removeRootView(containerTag);
},
unstable_createPortal(
children: ReactNodeList,
containerTag: number,
key: ?string = null,
) {
return ReactPortal.createPortal(children, containerTag, null, key);
},
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
};
if (typeof injectInternals === 'function') {
injectInternals({
findFiberByHostInstance: ReactNativeComponentTree.getClosestInstanceFromNode,
findHostInstanceByFiber: NativeRenderer.findHostInstance,
// This is an enum because we may add more (e.g. profiler build)
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
});
}
module.exports = ReactNative;

Просмотреть файл

@ -1,57 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeFiberErrorDialog
* @flow
*/
'use strict';
const ExceptionsManager = require('ExceptionsManager');
import type {CapturedError} from 'ReactFiberScheduler';
/**
* Intercept lifecycle errors and ensure they are shown with the correct stack
* trace within the native redbox component.
*/
function ReactNativeFiberErrorDialog(capturedError: CapturedError): boolean {
const {componentStack, error} = capturedError;
let errorMessage: string;
let errorStack: string;
let errorType: Class<Error>;
// Typically Errors are thrown but eg strings or null can be thrown as well.
if (error && typeof error === 'object') {
const {message, name} = error;
const summary = message ? `${name}: ${message}` : name;
errorMessage = `${summary}\n\nThis error is located at:${componentStack}`;
errorStack = error.stack;
errorType = error.constructor;
} else {
errorMessage = `Unspecified error at:${componentStack}`;
errorStack = '';
errorType = Error;
}
const newError = new errorType(errorMessage);
newError.stack = errorStack;
ExceptionsManager.handleException(newError, false);
// Return false here to prevent ReactFiberErrorLogger default behavior of
// logging error details to console.error. Calls to console.error are
// automatically routed to the native redbox controller, which we've already
// done above by calling ExceptionsManager.
return false;
}
module.exports.showDialog = ReactNativeFiberErrorDialog;

Просмотреть файл

@ -1,101 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeFiberHostComponent
* @flow
* @preventMunge
*/
'use strict';
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var TextInputState = require('TextInputState');
var UIManager = require('UIManager');
var {mountSafeCallback, warnForStyleProps} = require('NativeMethodsMixinUtils');
import type {
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
NativeMethodsInterface,
} from 'NativeMethodsMixinUtils';
import type {Instance} from 'ReactNativeFiber';
import type {
ReactNativeBaseComponentViewConfig,
} from 'ReactNativeViewConfigRegistry';
/**
* 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 implements NativeMethodsInterface {
_children: Array<Instance | number>;
_nativeTag: number;
viewConfig: ReactNativeBaseComponentViewConfig;
constructor(tag: number, viewConfig: ReactNativeBaseComponentViewConfig) {
this._nativeTag = tag;
this._children = [];
this.viewConfig = viewConfig;
}
blur() {
TextInputState.blurTextInput(this._nativeTag);
}
focus() {
TextInputState.focusTextInput(this._nativeTag);
}
measure(callback: MeasureOnSuccessCallback) {
UIManager.measure(this._nativeTag, mountSafeCallback(this, callback));
}
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
UIManager.measureInWindow(
this._nativeTag,
mountSafeCallback(this, callback),
);
}
measureLayout(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void /* currently unused */,
) {
UIManager.measureLayout(
this._nativeTag,
relativeToNativeNode,
mountSafeCallback(this, onFail),
mountSafeCallback(this, onSuccess),
);
}
setNativeProps(nativeProps: Object) {
if (__DEV__) {
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
}
var updatePayload = ReactNativeAttributePayload.create(
nativeProps,
this.viewConfig.validAttributes,
);
UIManager.updateView(
this._nativeTag,
this.viewConfig.uiViewClassName,
updatePayload,
);
}
}
module.exports = ReactNativeFiberHostComponent;

Просмотреть файл

@ -1,29 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeGlobalResponderHandler
*/
'use strict';
var UIManager = require('UIManager');
var ReactNativeGlobalResponderHandler = {
onChange: function(from, to, blockNativeResponder) {
if (to !== null) {
// TODO (bvaughn) Clean up once Stack is deprecated
var tag = typeof to.tag !== 'number'
? to._rootNodeID
: to.stateNode._nativeTag;
UIManager.setJSResponder(tag, blockNativeResponder);
} else {
UIManager.clearJSResponder();
}
},
};
module.exports = ReactNativeGlobalResponderHandler;

Просмотреть файл

@ -1,60 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeInjection
* @flow
*/
'use strict';
/**
* Make sure essential globals are available and are patched correctly. Please don't remove this
* line. Bundles created by react-packager `require` it before executing any application code. This
* ensures it exists in the dependency graph and can be `require`d.
* TODO: require this in packager, not in React #10932517
*/
require('InitializeCore');
var EventPluginHub = require('EventPluginHub');
var EventPluginUtils = require('EventPluginUtils');
var RCTEventEmitter = require('RCTEventEmitter');
var ReactNativeBridgeEventPlugin = require('ReactNativeBridgeEventPlugin');
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
var ReactNativeEventPluginOrder = require('ReactNativeEventPluginOrder');
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
var ResponderEventPlugin = require('ResponderEventPlugin');
function inject() {
/**
* Register the event emitter with the native bridge
*/
RCTEventEmitter.register(ReactNativeEventEmitter);
/**
* Inject module for resolving DOM hierarchy and plugin ordering.
*/
EventPluginHub.injection.injectEventPluginOrder(ReactNativeEventPluginOrder);
EventPluginUtils.injection.injectComponentTree(ReactNativeComponentTree);
ResponderEventPlugin.injection.injectGlobalResponderHandler(
ReactNativeGlobalResponderHandler,
);
/**
* Some important event plugins included by default (without having to require
* them).
*/
EventPluginHub.injection.injectEventPluginsByName({
ResponderEventPlugin: ResponderEventPlugin,
ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin,
});
}
module.exports = {
inject: inject,
};

Просмотреть файл

@ -1,227 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeMount
* @flow
*/
'use strict';
var React = require('react');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactNativeContainerInfo = require('ReactNativeContainerInfo');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
var UIManager = require('UIManager');
var emptyObject = require('fbjs/lib/emptyObject');
var instantiateReactComponent = require('instantiateReactComponent');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
/**
* Temporary (?) hack so that we can store all top-level pending updates on
* composites instead of having to worry about different types of components
* here.
*/
var TopLevelWrapper = function() {};
TopLevelWrapper.prototype.isReactComponent = {};
if (__DEV__) {
TopLevelWrapper.displayName = 'TopLevelWrapper';
}
TopLevelWrapper.prototype.render = function() {
return this.props.child;
};
TopLevelWrapper.isReactTopLevelWrapper = true;
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} containerTag container element to mount into.
* @param {ReactReconcileTransaction} transaction
*/
function mountComponentIntoNode(componentInstance, containerTag, transaction) {
var markup = ReactReconciler.mountComponent(
componentInstance,
transaction,
null,
ReactNativeContainerInfo(containerTag),
emptyObject,
0 /* parentDebugID */,
);
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
ReactNativeMount._mountImageIntoNode(markup, containerTag);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} containerTag container element to mount into.
*/
function batchedMountComponentIntoNode(componentInstance, containerTag) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
containerTag,
transaction,
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* As soon as `ReactMount` is refactored to not rely on the DOM, we can share
* code between the two. For now, we'll hard code the ID logic.
*/
var ReactNativeMount = {
_instancesByContainerID: {},
// these two functions are needed by React Devtools
findNodeHandle: require('findNodeHandle'),
/**
* @param {ReactComponent} instance Instance to render.
* @param {containerTag} containerView Handle to native view tag
*/
renderComponent: function(
nextElement: ReactElement<*>,
containerTag: number,
callback?: ?() => void,
): ?ReactComponent<any, any, any> {
var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement,
});
var topRootNodeID = containerTag;
var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props.child;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
ReactUpdateQueue.enqueueElementInternal(
prevComponent,
nextWrappedElement,
emptyObject,
);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
return prevComponent;
} else {
ReactNativeMount.unmountComponentAtNode(containerTag);
}
}
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return null;
}
ReactNativeTagHandles.assertRootTag(containerTag);
var instance = instantiateReactComponent(nextWrappedElement, false);
ReactNativeMount._instancesByContainerID[containerTag] = instance;
if (callback) {
var nonNullCallback = callback;
instance._pendingCallbacks = [
function() {
nonNullCallback.call(instance._renderedComponent.getPublicInstance());
},
];
}
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
instance,
containerTag,
);
var component = instance._renderedComponent.getPublicInstance();
return component;
},
/**
* @param {View} view View tree image.
* @param {number} containerViewID View to insert sub-view into.
*/
_mountImageIntoNode: function(mountImage: number, containerID: number) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
var childTag = mountImage;
UIManager.setChildren(containerID, [childTag]);
},
/**
* Standard unmounting of the component that is rendered into `containerID`,
* but will also execute a command to remove the actual container view
* itself. This is useful when a client is cleaning up a React tree, and also
* knows that the container will no longer be needed. When executing
* asynchronously, it's easier to just have this method be the one that calls
* for removal of the view.
*/
unmountComponentAtNodeAndRemoveContainer: function(containerTag: number) {
ReactNativeMount.unmountComponentAtNode(containerTag);
// call back into native to remove all of the subviews from this container
UIManager.removeRootView(containerTag);
},
/**
* Unmount component at container ID by iterating through each child component
* that has been rendered and unmounting it. There should just be one child
* component at this time.
*/
unmountComponentAtNode: function(containerTag: number): boolean {
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return false;
}
var instance = ReactNativeMount._instancesByContainerID[containerTag];
if (!instance) {
return false;
}
if (__DEV__) {
ReactInstrumentation.debugTool.onBeginFlush();
}
ReactNativeMount.unmountComponentFromNode(instance, containerTag);
delete ReactNativeMount._instancesByContainerID[containerTag];
if (__DEV__) {
ReactInstrumentation.debugTool.onEndFlush();
}
return true;
},
/**
* Unmounts a component and sends messages back to iOS to remove its subviews.
*
* @param {ReactComponent} instance React component instance.
* @param {string} containerID ID of container we're removing from.
* @final
* @internal
* @see {ReactNativeMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(
instance: ReactComponent<any, any, any>,
containerID: number,
) {
// Call back into native to remove all of the subviews from this container
ReactReconciler.unmountComponent(instance);
UIManager.removeSubviewsFromContainerWithID(containerID);
},
};
module.exports = ReactNativeMount;

Просмотреть файл

@ -1,44 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativePropRegistry
* @flow
*/
'use strict';
var objects = {};
var uniqueID = 1;
var emptyObject = {};
class ReactNativePropRegistry {
static register(object: Object): number {
var id = ++uniqueID;
if (__DEV__) {
Object.freeze(object);
}
objects[id] = object;
return id;
}
static getByID(id: number): Object {
if (!id) {
// Used in the style={[condition && id]} pattern,
// we want it to be a no-op when the value is false or null
return emptyObject;
}
var object = objects[id];
if (!object) {
console.warn('Invalid style with id `' + id + '`. Skipping ...');
return emptyObject;
}
return object;
}
}
module.exports = ReactNativePropRegistry;

Просмотреть файл

@ -1,132 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeReconcileTransaction
* @flow
*/
'use strict';
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactUpdateQueue = require('ReactUpdateQueue');
/**
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks during
* the performing of the transaction.
*/
var ON_DOM_READY_QUEUEING = {
/**
* Initializes the internal `onDOMReady` queue.
*/
initialize: function() {
this.reactMountReady.reset();
},
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function() {
this.reactMountReady.notifyAll();
},
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
if (__DEV__) {
TRANSACTION_WRAPPERS.push({
initialize: ReactInstrumentation.debugTool.onBeginFlush,
close: ReactInstrumentation.debugTool.onEndFlush,
});
}
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactNativeReconcileTransaction
*/
function ReactNativeReconcileTransaction() {
this.reinitializeTransaction();
this.reactMountReady = CallbackQueue.getPooled(null);
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap procedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
* TODO: convert to ReactMountReady
*/
getReactMountReady: function() {
return this.reactMountReady;
},
/**
* @return {object} The queue to collect React async events.
*/
getUpdateQueue: function() {
return ReactUpdateQueue;
},
/**
* Save current transaction state -- if the return value from this method is
* passed to `rollback`, the transaction will be reset to that state.
*/
checkpoint: function() {
// reactMountReady is the our only stateful wrapper
return this.reactMountReady.checkpoint();
},
rollback: function(checkpoint) {
this.reactMountReady.rollback(checkpoint);
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be reused.
*/
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
},
};
Object.assign(
ReactNativeReconcileTransaction.prototype,
Transaction,
ReactNativeReconcileTransaction,
Mixin,
);
PooledClass.addPoolingTo(ReactNativeReconcileTransaction);
module.exports = ReactNativeReconcileTransaction;

Просмотреть файл

@ -1,87 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeStack
* @flow
*/
'use strict';
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeInjection = require('ReactNativeInjection');
var ReactNativeMount = require('ReactNativeMount');
var ReactNativeStackInjection = require('ReactNativeStackInjection');
var ReactUpdates = require('ReactUpdates');
var findNodeHandle = require('findNodeHandle');
ReactNativeInjection.inject();
ReactNativeStackInjection.inject();
var render = function(
element: ReactElement<any>,
mountInto: number,
callback?: ?() => void,
): ?ReactComponent<any, any, any> {
return ReactNativeMount.renderComponent(element, mountInto, callback);
};
var ReactNative = {
hasReactNativeInitialized: false,
// External users of findNodeHandle() expect the host tag number return type.
// The injected findNodeHandle() strategy returns the instance wrapper though.
// See NativeMethodsMixin#setNativeProps for more info on why this is done.
findNodeHandle(componentOrHandle: any): ?number {
const nodeHandle = findNodeHandle(componentOrHandle);
if (nodeHandle == null || typeof nodeHandle === 'number') {
return nodeHandle;
}
return nodeHandle.getHostNode();
},
render: render,
unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
/* eslint-disable camelcase */
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
/* eslint-enable camelcase */
unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
};
// Inject the runtime into a devtools global hook regardless of browser.
// Allows for debugging when the hook is injected on the page.
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
ComponentTree: {
getClosestInstanceFromNode: function(node) {
return ReactNativeComponentTree.getClosestInstanceFromNode(node);
},
getNodeFromInstance: function(inst) {
// inst is an internal instance (but could be a composite)
while (inst._renderedComponent) {
inst = inst._renderedComponent;
}
if (inst) {
return ReactNativeComponentTree.getNodeFromInstance(inst);
} else {
return null;
}
},
},
Mount: ReactNativeMount,
Reconciler: require('ReactReconciler'),
});
}
module.exports = ReactNative;

Просмотреть файл

@ -1,83 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeStackInjection
* @flow
*/
'use strict';
/**
* Make sure essential globals are available and are patched correctly. Please don't remove this
* line. Bundles created by react-packager `require` it before executing any application code. This
* ensures it exists in the dependency graph and can be `require`d.
* TODO: require this in packager, not in React #10932517
*/
require('InitializeCore');
var React = require('react');
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactHostComponent = require('ReactHostComponent');
var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
var ReactNativeTextComponent = require('ReactNativeTextComponent');
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
var ReactUpdates = require('ReactUpdates');
var findNodeHandle = require('findNodeHandle');
var invariant = require('fbjs/lib/invariant');
function inject() {
ReactGenericBatching.injection.injectStackBatchedUpdates(
ReactUpdates.batchedUpdates,
);
ReactUpdates.injection.injectReconcileTransaction(
ReactNativeComponentEnvironment.ReactReconcileTransaction,
);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
ReactComponentEnvironment.injection.injectEnvironment(
ReactNativeComponentEnvironment,
);
var EmptyComponent = instantiate => {
// Can't import View at the top because it depends on React to make its composite
var View = require('View');
return new ReactSimpleEmptyComponent(
React.createElement(View, {
collapsable: true,
style: {position: 'absolute'},
}),
instantiate,
);
};
findNodeHandle.injection.injectFindNode(instance => instance);
findNodeHandle.injection.injectFindRootNodeID(instance => instance);
ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent);
ReactHostComponent.injection.injectTextComponentClass(
ReactNativeTextComponent,
);
ReactHostComponent.injection.injectGenericComponentClass(function(tag) {
// Show a nicer error message for non-function tags
var info = '';
if (typeof tag === 'string' && /^[a-z]/.test(tag)) {
info += ' Each component name should start with an uppercase letter.';
}
invariant(false, 'Expected a component class, got %s.%s', tag, info);
});
}
module.exports = {
inject: inject,
};

Просмотреть файл

@ -1,58 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTagHandles
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
* collisions, but in the JS we keep track of them as positive integers to store
* them effectively in Arrays. So we must refer to them as "inverses" of the
* native tags (that are * normally negative).
*
* It *must* be the case that every `rootNodeID` always maps to the exact same
* `tag` forever. The easiest way to accomplish this is to never delete
* anything from this table.
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
var INITIAL_TAG_COUNT = 1;
var ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
var tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
assertRootTag: function(tag: number): void {
invariant(
this.reactTagIsNativeTopRootID(tag),
'Expect a native root tag, instead got %s',
tag,
);
},
reactTagIsNativeTopRootID: function(reactTag: number): boolean {
// We reserve all tags that are 1 mod 10 for native root views
return reactTag % 10 === 1;
},
};
module.exports = ReactNativeTagHandles;

Просмотреть файл

@ -1,79 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTextComponent
*/
'use strict';
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var UIManager = require('UIManager');
var invariant = require('fbjs/lib/invariant');
var ReactNativeTextComponent = function(text) {
// This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
this._hostParent = null;
this._rootNodeID = 0;
};
Object.assign(ReactNativeTextComponent.prototype, {
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
) {
// TODO: hostParent should have this context already. Stop abusing context.
invariant(
context.isInAParentText,
'RawText "%s" must be wrapped in an explicit <Text> component.',
this._stringText,
);
this._hostParent = hostParent;
var tag = ReactNativeTagHandles.allocateTag();
this._rootNodeID = tag;
var nativeTopRootTag = hostContainerInfo._tag;
UIManager.createView(tag, 'RCTRawText', nativeTopRootTag, {
text: this._stringText,
});
ReactNativeComponentTree.precacheNode(this, tag);
return tag;
},
getHostNode: function() {
return this._rootNodeID;
},
receiveComponent: function(nextText, transaction, context) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
UIManager.updateView(this._rootNodeID, 'RCTRawText', {
text: this._stringText,
});
}
}
},
unmountComponent: function() {
ReactNativeComponentTree.uncacheNode(this);
this._currentElement = null;
this._stringText = null;
this._rootNodeID = 0;
},
});
module.exports = ReactNativeTextComponent;

Просмотреть файл

@ -1,46 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeViewConfigRegistry
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
export type ReactNativeBaseComponentViewConfig = {
validAttributes: Object,
uiViewClassName: string,
propTypes?: Object,
};
const viewConfigs = new Map();
const prefix = 'topsecret-';
const ReactNativeViewConfigRegistry = {
register(viewConfig: ReactNativeBaseComponentViewConfig) {
const name = viewConfig.uiViewClassName;
invariant(
!viewConfigs.has(name),
'Tried to register two views with the same name %s',
name,
);
const secretName = prefix + name;
viewConfigs.set(secretName, viewConfig);
return secretName;
},
get(secretName: string) {
const config = viewConfigs.get(secretName);
invariant(config, 'View config not found for name %s', secretName);
return config;
},
};
module.exports = ReactNativeViewConfigRegistry;

Просмотреть файл

@ -1,16 +0,0 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
module.exports = ReactNativeFeatureFlags.useFiber
? require('ReactNativeFiber')
: require('ReactNativeStack');

Просмотреть файл

@ -1,236 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
'use strict';
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var ReactNativePropRegistry = require('ReactNativePropRegistry');
var diff = ReactNativeAttributePayload.diff;
describe('ReactNativeAttributePayload', () => {
it('should work with simple example', () => {
expect(diff({a: 1, c: 3}, {b: 2, c: 3}, {a: true, b: true})).toEqual({
a: null,
b: 2,
});
});
it('should skip fields that are equal', () => {
expect(
diff(
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: true, b: true, c: true, d: true, e: true, f: true},
),
).toEqual(null);
});
it('should remove fields', () => {
expect(diff({a: 1}, {}, {a: true})).toEqual({a: null});
});
it('should remove fields that are set to undefined', () => {
expect(diff({a: 1}, {a: undefined}, {a: true})).toEqual({a: null});
});
it('should ignore invalid fields', () => {
expect(diff({a: 1}, {b: 2}, {})).toEqual(null);
});
it('should use the diff attribute', () => {
var diffA = jest.fn((a, b) => true);
var diffB = jest.fn((a, b) => false);
expect(
diff(
{a: [1], b: [3]},
{a: [2], b: [4]},
{a: {diff: diffA}, b: {diff: diffB}},
),
).toEqual({a: [2]});
expect(diffA).toBeCalledWith([1], [2]);
expect(diffB).toBeCalledWith([3], [4]);
});
it('should not use the diff attribute on addition/removal', () => {
var diffA = jest.fn();
var diffB = jest.fn();
expect(
diff({a: [1]}, {b: [2]}, {a: {diff: diffA}, b: {diff: diffB}}),
).toEqual({a: null, b: [2]});
expect(diffA).not.toBeCalled();
expect(diffB).not.toBeCalled();
});
it('should do deep diffs of Objects by default', () => {
expect(
diff(
{a: [1], b: {k: [3, 4]}, c: {k: [4, 4]}},
{a: [2], b: {k: [3, 4]}, c: {k: [4, 5]}},
{a: true, b: true, c: true},
),
).toEqual({a: [2], c: {k: [4, 5]}});
});
it('should work with undefined styles', () => {
expect(
diff(
{style: {a: '#ffffff', b: 1}},
{style: undefined},
{style: {b: true}},
),
).toEqual({b: null});
expect(
diff(
{style: undefined},
{style: {a: '#ffffff', b: 1}},
{style: {b: true}},
),
).toEqual({b: 1});
expect(
diff({style: undefined}, {style: undefined}, {style: {b: true}}),
).toEqual(null);
});
it('should work with empty styles', () => {
expect(diff({a: 1, c: 3}, {}, {a: true, b: true})).toEqual({a: null});
expect(diff({}, {a: 1, c: 3}, {a: true, b: true})).toEqual({a: 1});
expect(diff({}, {}, {a: true, b: true})).toEqual(null);
});
it('should flatten nested styles and predefined styles', () => {
var validStyleAttribute = {someStyle: {foo: true, bar: true}};
expect(
diff({}, {someStyle: [{foo: 1}, {bar: 2}]}, validStyleAttribute),
).toEqual({foo: 1, bar: 2});
expect(
diff({someStyle: [{foo: 1}, {bar: 2}]}, {}, validStyleAttribute),
).toEqual({foo: null, bar: null});
var barStyle = ReactNativePropRegistry.register({
bar: 3,
});
expect(
diff(
{},
{someStyle: [[{foo: 1}, {foo: 2}], barStyle]},
validStyleAttribute,
),
).toEqual({foo: 2, bar: 3});
});
it('should reset a value to a previous if it is removed', () => {
var validStyleAttribute = {someStyle: {foo: true, bar: true}};
expect(
diff(
{someStyle: [{foo: 1}, {foo: 3}]},
{someStyle: [{foo: 1}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: 1, bar: 2});
});
it('should not clear removed props if they are still in another slot', () => {
var validStyleAttribute = {someStyle: {foo: true, bar: true}};
expect(
diff(
{someStyle: [{}, {foo: 3, bar: 2}]},
{someStyle: [{foo: 3}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: 3}); // this should ideally be null. heuristic tradeoff.
expect(
diff(
{someStyle: [{}, {foo: 3, bar: 2}]},
{someStyle: [{foo: 1, bar: 1}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({bar: 2, foo: 1});
});
it('should clear a prop if a later style is explicit null/undefined', () => {
var validStyleAttribute = {someStyle: {foo: true, bar: true}};
expect(
diff(
{someStyle: [{}, {foo: 3, bar: 2}]},
{someStyle: [{foo: 1}, {bar: 2, foo: null}]},
validStyleAttribute,
),
).toEqual({foo: null});
expect(
diff(
{someStyle: [{foo: 3}, {foo: null, bar: 2}]},
{someStyle: [{foo: null}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: null});
expect(
diff(
{someStyle: [{foo: 1}, {foo: null}]},
{someStyle: [{foo: 2}, {foo: null}]},
validStyleAttribute,
),
).toEqual({foo: null}); // this should ideally be null. heuristic.
// Test the same case with object equality because an early bailout doesn't
// work in this case.
var fooObj = {foo: 3};
expect(
diff(
{someStyle: [{foo: 1}, fooObj]},
{someStyle: [{foo: 2}, fooObj]},
validStyleAttribute,
),
).toEqual({foo: 3}); // this should ideally be null. heuristic.
expect(
diff(
{someStyle: [{foo: 1}, {foo: 3}]},
{someStyle: [{foo: 2}, {foo: undefined}]},
validStyleAttribute,
),
).toEqual({foo: null}); // this should ideally be null. heuristic.
});
// Function properties are just markers to native that events should be sent.
it('should convert functions to booleans', () => {
// Note that if the property changes from one function to another, we don't
// need to send an update.
expect(
diff(
{
a: function() {
return 1;
},
b: function() {
return 2;
},
c: 3,
},
{
b: function() {
return 9;
},
c: function() {
return 3;
},
},
{a: true, b: true, c: true},
),
).toEqual({a: null, c: true});
});
});

Просмотреть файл

@ -1,366 +0,0 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var PropTypes;
var RCTEventEmitter;
var React;
var ReactNative;
var ResponderEventPlugin;
var UIManager;
var createReactNativeComponentClass;
beforeEach(() => {
jest.resetModules();
PropTypes = require('prop-types');
RCTEventEmitter = require('RCTEventEmitter');
React = require('react');
ReactNative = require('ReactNative');
ResponderEventPlugin = require('ResponderEventPlugin');
UIManager = require('UIManager');
createReactNativeComponentClass = require('createReactNativeComponentClass');
});
it('handles events', () => {
expect(RCTEventEmitter.register.mock.calls.length).toBe(1);
var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
var View = createReactNativeComponentClass({
validAttributes: {foo: true},
uiViewClassName: 'View',
});
var log = [];
ReactNative.render(
<View
foo="outer"
onTouchEnd={() => log.push('outer touchend')}
onTouchEndCapture={() => log.push('outer touchend capture')}
onTouchStart={() => log.push('outer touchstart')}
onTouchStartCapture={() => log.push('outer touchstart capture')}>
<View
foo="inner"
onTouchEndCapture={() => log.push('inner touchend capture')}
onTouchEnd={() => log.push('inner touchend')}
onTouchStartCapture={() => log.push('inner touchstart capture')}
onTouchStart={() => log.push('inner touchstart')}
/>
</View>,
1,
);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
expect(UIManager.createView.mock.calls.length).toBe(2);
// Don't depend on the order of createView() calls.
// Stack creates views outside-in; fiber creates them inside-out.
var innerTag = UIManager.createView.mock.calls.find(
args => args[3].foo === 'inner',
)[0];
EventEmitter.receiveTouches(
'topTouchStart',
[{target: innerTag, identifier: 17}],
[0],
);
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: innerTag, identifier: 17}],
[0],
);
expect(log).toEqual([
'outer touchstart capture',
'inner touchstart capture',
'inner touchstart',
'outer touchstart',
'outer touchend capture',
'inner touchend capture',
'inner touchend',
'outer touchend',
]);
});
it('handles events on text nodes', () => {
expect(RCTEventEmitter.register.mock.calls.length).toBe(1);
var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
var Text = createReactNativeComponentClass({
validAttributes: {foo: true},
uiViewClassName: 'Text',
});
class ContextHack extends React.Component {
static childContextTypes = {isInAParentText: PropTypes.bool};
getChildContext() {
return {isInAParentText: true};
}
render() {
return this.props.children;
}
}
var log = [];
ReactNative.render(
<ContextHack>
<Text>
<Text
onTouchEnd={() => log.push('string touchend')}
onTouchEndCapture={() => log.push('string touchend capture')}
onTouchStart={() => log.push('string touchstart')}
onTouchStartCapture={() => log.push('string touchstart capture')}>
Text Content
</Text>
<Text
onTouchEnd={() => log.push('number touchend')}
onTouchEndCapture={() => log.push('number touchend capture')}
onTouchStart={() => log.push('number touchstart')}
onTouchStartCapture={() => log.push('number touchstart capture')}>
{123}
</Text>
</Text>
</ContextHack>,
1,
);
expect(UIManager.createView.mock.calls.length).toBe(5);
// Don't depend on the order of createView() calls.
// Stack creates views outside-in; fiber creates them inside-out.
var innerTagString = UIManager.createView.mock.calls.find(
args => args[3] && args[3].text === 'Text Content',
)[0];
var innerTagNumber = UIManager.createView.mock.calls.find(
args => args[3] && args[3].text === '123',
)[0];
EventEmitter.receiveTouches(
'topTouchStart',
[{target: innerTagString, identifier: 17}],
[0],
);
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: innerTagString, identifier: 17}],
[0],
);
EventEmitter.receiveTouches(
'topTouchStart',
[{target: innerTagNumber, identifier: 18}],
[0],
);
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: innerTagNumber, identifier: 18}],
[0],
);
expect(log).toEqual([
'string touchstart capture',
'string touchstart',
'string touchend capture',
'string touchend',
'number touchstart capture',
'number touchstart',
'number touchend capture',
'number touchend',
]);
});
it('handles when a responder is unmounted while a touch sequence is in progress', () => {
var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
var View = createReactNativeComponentClass({
validAttributes: {id: true},
uiViewClassName: 'View',
});
function getViewById(id) {
return UIManager.createView.mock.calls.find(
args => args[3] && args[3].id === id,
)[0];
}
function getResponderId() {
const responder = ResponderEventPlugin._getResponder();
if (responder === null) {
return null;
}
const props = typeof responder.tag === 'number'
? responder.memoizedProps
: responder._currentElement.props;
return props ? props.id : null;
}
var log = [];
ReactNative.render(
<View id="parent">
<View key={1}>
<View
id="one"
onResponderEnd={() => log.push('one responder end')}
onResponderStart={() => log.push('one responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
1,
);
EventEmitter.receiveTouches(
'topTouchStart',
[{target: getViewById('one'), identifier: 17}],
[0],
);
expect(getResponderId()).toBe('one');
expect(log).toEqual(['one responder start']);
log.splice(0);
ReactNative.render(
<View id="parent">
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
1,
);
// TODO Verify the onResponderEnd listener has been called (before the unmount)
// expect(log).toEqual(['one responder end']);
// log.splice(0);
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: getViewById('two'), identifier: 17}],
[0],
);
expect(getResponderId()).toBeNull();
expect(log).toEqual([]);
EventEmitter.receiveTouches(
'topTouchStart',
[{target: getViewById('two'), identifier: 17}],
[0],
);
expect(getResponderId()).toBe('two');
expect(log).toEqual(['two responder start']);
});
it('handles events without target', () => {
var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
var View = createReactNativeComponentClass({
validAttributes: {id: true},
uiViewClassName: 'View',
});
function getViewById(id) {
return UIManager.createView.mock.calls.find(
args => args[3] && args[3].id === id,
)[0];
}
function getResponderId() {
const responder = ResponderEventPlugin._getResponder();
if (responder === null) {
return null;
}
const props = typeof responder.tag === 'number'
? responder.memoizedProps
: responder._currentElement.props;
return props ? props.id : null;
}
var log = [];
function render(renderFirstComponent) {
ReactNative.render(
<View id="parent">
<View key={1}>
{renderFirstComponent
? <View
id="one"
onResponderEnd={() => log.push('one responder end')}
onResponderStart={() => log.push('one responder start')}
onStartShouldSetResponder={() => true}
/>
: null}
</View>
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
1,
);
}
render(true);
EventEmitter.receiveTouches(
'topTouchStart',
[{target: getViewById('one'), identifier: 17}],
[0],
);
// Unmounting component 'one'.
render(false);
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: getViewById('one'), identifier: 17}],
[0],
);
expect(getResponderId()).toBe(null);
EventEmitter.receiveTouches(
'topTouchStart',
[{target: getViewById('two'), identifier: 18}],
[0],
);
expect(getResponderId()).toBe('two');
EventEmitter.receiveTouches(
'topTouchEnd',
[{target: getViewById('two'), identifier: 18}],
[0],
);
expect(getResponderId()).toBe(null);
expect(log).toEqual([
'one responder start',
'two responder start',
'two responder end',
]);
});

Просмотреть файл

@ -1,111 +0,0 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNative;
var createReactNativeComponentClass;
var UIManager;
describe('ReactNative', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNative = require('ReactNative');
UIManager = require('UIManager');
createReactNativeComponentClass = require('createReactNativeComponentClass');
});
it('should be able to create and render a native component', () => {
var View = createReactNativeComponentClass({
validAttributes: {foo: true},
uiViewClassName: 'View',
});
ReactNative.render(<View foo="test" />, 1);
expect(UIManager.createView).toBeCalled();
expect(UIManager.setChildren).toBeCalled();
expect(UIManager.manageChildren).not.toBeCalled();
expect(UIManager.updateView).not.toBeCalled();
});
it('should be able to create and update a native component', () => {
var View = createReactNativeComponentClass({
validAttributes: {foo: true},
uiViewClassName: 'View',
});
ReactNative.render(<View foo="foo" />, 11);
expect(UIManager.createView.mock.calls.length).toBe(1);
expect(UIManager.setChildren.mock.calls.length).toBe(1);
expect(UIManager.manageChildren).not.toBeCalled();
expect(UIManager.updateView).not.toBeCalled();
ReactNative.render(<View foo="bar" />, 11);
expect(UIManager.createView.mock.calls.length).toBe(1);
expect(UIManager.setChildren.mock.calls.length).toBe(1);
expect(UIManager.manageChildren).not.toBeCalled();
expect(UIManager.updateView).toBeCalledWith(2, 'View', {foo: 'bar'});
});
it('returns the correct instance and calls it in the callback', () => {
var View = createReactNativeComponentClass({
validAttributes: {foo: true},
uiViewClassName: 'View',
});
var a;
var b;
var c = ReactNative.render(
<View foo="foo" ref={v => (a = v)} />,
11,
function() {
b = this;
},
);
expect(a).toBeTruthy();
expect(a).toBe(b);
expect(a).toBe(c);
});
it('renders and reorders children', () => {
var View = createReactNativeComponentClass({
validAttributes: {title: true},
uiViewClassName: 'View',
});
class Component extends React.Component {
render() {
var chars = this.props.chars.split('');
return (
<View>
{chars.map(text => <View key={text} title={text} />)}
</View>
);
}
}
// Mini multi-child stress test: lots of reorders, some adds, some removes.
var before = 'abcdefghijklmnopqrst';
var after = 'mxhpgwfralkeoivcstzy';
ReactNative.render(<Component chars={before} />, 11);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
ReactNative.render(<Component chars={after} />, 11);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
});
});

Просмотреть файл

@ -1,7 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`handles events 1`] = `
"<native root> {}
View {\\"foo\\":\\"outer\\"}
View {\\"foo\\":\\"inner\\"}"
`;

Просмотреть файл

@ -1,51 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReactNative renders and reorders children 1`] = `
"<native root> {}
View null
View {\\"title\\":\\"a\\"}
View {\\"title\\":\\"b\\"}
View {\\"title\\":\\"c\\"}
View {\\"title\\":\\"d\\"}
View {\\"title\\":\\"e\\"}
View {\\"title\\":\\"f\\"}
View {\\"title\\":\\"g\\"}
View {\\"title\\":\\"h\\"}
View {\\"title\\":\\"i\\"}
View {\\"title\\":\\"j\\"}
View {\\"title\\":\\"k\\"}
View {\\"title\\":\\"l\\"}
View {\\"title\\":\\"m\\"}
View {\\"title\\":\\"n\\"}
View {\\"title\\":\\"o\\"}
View {\\"title\\":\\"p\\"}
View {\\"title\\":\\"q\\"}
View {\\"title\\":\\"r\\"}
View {\\"title\\":\\"s\\"}
View {\\"title\\":\\"t\\"}"
`;
exports[`ReactNative renders and reorders children 2`] = `
"<native root> {}
View null
View {\\"title\\":\\"m\\"}
View {\\"title\\":\\"x\\"}
View {\\"title\\":\\"h\\"}
View {\\"title\\":\\"p\\"}
View {\\"title\\":\\"g\\"}
View {\\"title\\":\\"w\\"}
View {\\"title\\":\\"f\\"}
View {\\"title\\":\\"r\\"}
View {\\"title\\":\\"a\\"}
View {\\"title\\":\\"l\\"}
View {\\"title\\":\\"k\\"}
View {\\"title\\":\\"e\\"}
View {\\"title\\":\\"o\\"}
View {\\"title\\":\\"i\\"}
View {\\"title\\":\\"v\\"}
View {\\"title\\":\\"c\\"}
View {\\"title\\":\\"s\\"}
View {\\"title\\":\\"t\\"}
View {\\"title\\":\\"z\\"}
View {\\"title\\":\\"y\\"}"
`;

Просмотреть файл

@ -1,62 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createReactNativeComponentClass
* @flow
*/
'use strict';
const ReactNativeBaseComponent = require('ReactNativeBaseComponent');
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
// See also ReactNativeBaseComponent
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object,
uiViewClassName: string,
propTypes?: Object,
};
/**
* @param {string} config iOS View configuration.
* @private
*/
const createReactNativeFiberComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig,
): string {
return ReactNativeViewConfigRegistry.register(viewConfig);
};
/**
* @param {string} config iOS View configuration.
* @private
*/
const createReactNativeComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig,
): ReactClass<any> {
const Constructor = function(element) {
this._currentElement = element;
this._topLevelWrapper = null;
this._hostParent = null;
this._hostContainerInfo = null;
this._rootNodeID = 0;
this._renderedChildren = null;
};
Constructor.displayName = viewConfig.uiViewClassName;
Constructor.viewConfig = viewConfig;
Constructor.propTypes = viewConfig.propTypes;
Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
Constructor.prototype.constructor = Constructor;
return ((Constructor: any): ReactClass<any>);
};
module.exports = ReactNativeFeatureFlags.useFiber
? createReactNativeFiberComponentClass
: createReactNativeComponentClass;

Просмотреть файл

@ -1,139 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule findNodeHandle
* @flow
*/
'use strict';
var ReactInstanceMap = require('ReactInstanceMap');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var getComponentName = require('getComponentName');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
import type {Fiber} from 'ReactFiber';
import type {ReactInstance} from 'ReactInstanceType';
/**
* ReactNative vs ReactWeb
* -----------------------
* React treats some pieces of data opaquely. This means that the information
* is first class (it can be passed around), but cannot be inspected. This
* allows us to build infrastructure that reasons about resources, without
* making assumptions about the nature of those resources, and this allows that
* infra to be shared across multiple platforms, where the resources are very
* different. General infra (such as `ReactMultiChild`) reasons opaquely about
* the data, but platform specific code (such as `ReactNativeBaseComponent`) can
* make assumptions about the data.
*
*
* `rootNodeID`, uniquely identifies a position in the generated native view
* tree. Many layers of composite components (created with `React.createClass`)
* can all share the same `rootNodeID`.
*
* `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
* resource (dom node, native view etc). The `rootNodeID` is sufficient for web
* `nodeHandle`s, because the position in a tree is always enough to uniquely
* identify a DOM node (we never have nodes in some bank outside of the
* document). The same would be true for `ReactNative`, but we must maintain a
* mapping that we can send efficiently serializable
* strings across native boundaries.
*
* Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
* ----------------------------------------------------------------------------
* nodeHandle N/A rootNodeID tag
*/
let injectedFindNode;
let injectedFindRootNodeID;
// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive
// eg findInternalHostInstance. This will reduce the likelihood of someone
// accidentally deep-requiring this version.
function findNodeHandle(componentOrHandle: any): any {
if (__DEV__) {
var owner =
((ReactCurrentOwner.current: any): ReactInstance | Fiber | null);
if (owner !== null) {
const isFiber = typeof (owner: any).tag === 'number';
const warnedAboutRefsInRender = isFiber
? ((owner: any): Fiber).stateNode._warnedAboutRefsInRender
: ((owner: any): ReactInstance)._warnedAboutRefsInRender;
warning(
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) || 'A component',
);
if (isFiber) {
((owner: any): Fiber).stateNode._warnedAboutRefsInRender = true;
} else {
((owner: any): ReactInstance)._warnedAboutRefsInRender = true;
}
}
}
if (componentOrHandle == null) {
return null;
}
if (typeof componentOrHandle === 'number') {
// Already a node handle
return componentOrHandle;
}
var component = componentOrHandle;
// TODO (balpert): Wrap iOS native components in a composite wrapper, then
// ReactInstanceMap.get here will always succeed for mounted components
var internalInstance = ReactInstanceMap.get(component);
if (internalInstance) {
return injectedFindNode(internalInstance);
} else {
var rootNodeID = injectedFindRootNodeID(component);
if (rootNodeID) {
return rootNodeID;
} else {
invariant(
// Native
(typeof component === 'object' &&
('_rootNodeID' in component || // TODO (bvaughn) Clean up once Stack is deprecated
'_nativeTag' in component)) ||
// Composite
(component.render != null && typeof component.render === 'function'),
'findNodeHandle(...): Argument is not a component ' +
'(type: %s, keys: %s)',
typeof component,
Object.keys(component),
);
invariant(
false,
'findNodeHandle(...): Unable to find node handle for unmounted ' +
'component.',
);
}
}
}
// Fiber and stack implementations differ; each must inject a strategy
findNodeHandle.injection = {
injectFindNode(findNode) {
injectedFindNode = findNode;
},
injectFindRootNodeID(findRootNodeID) {
injectedFindRootNodeID = findRootNodeID;
},
};
module.exports = findNodeHandle;

Просмотреть файл

@ -1,54 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule takeSnapshot
* @flow
*/
'use strict';
var ReactNative = require('ReactNative');
var UIManager = require('UIManager');
import type {Element} from 'React';
/**
* Capture an image of the screen, window or an individual view. The image
* will be stored in a temporary file that will only exist for as long as the
* app is running.
*
* The `view` argument can be the literal string `window` if you want to
* capture the entire window, or it can be a reference to a specific
* React Native component.
*
* The `options` argument may include:
* - width/height (number) - the width and height of the image to capture.
* - format (string) - either 'png' or 'jpeg'. Defaults to 'png'.
* - quality (number) - the quality when using jpeg. 0.0 - 1.0 (default).
*
* Returns a Promise.
* @platform ios
*/
function takeSnapshot(
view?: 'window' | Element<any> | number,
options?: {
width?: number,
height?: number,
format?: 'png' | 'jpeg',
quality?: number,
},
): Promise<any> {
if (typeof view !== 'number' && view !== 'window') {
view = ReactNative.findNodeHandle(view) || 'window';
}
// Call the hidden '__takeSnapshot' method; the main one throws an error to
// prevent accidental backwards-incompatible usage.
return UIManager.__takeSnapshot(view, options);
}
module.exports = takeSnapshot;

Просмотреть файл

@ -1,391 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNoop
* @flow
*/
/**
* This is a renderer of React that doesn't have a render target output.
* It is useful to demonstrate the internals of the reconciler in isolation
* and for testing semantics of reconciliation separate from the host
* environment.
*/
'use strict';
import type { Fiber } from 'ReactFiber';
import type { UpdateQueue } from 'ReactFiberUpdateQueue';
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
var ReactFiberReconciler = require('ReactFiberReconciler');
var ReactInstanceMap = require('ReactInstanceMap');
var {
AnimationPriority,
} = require('ReactPriorityLevel');
var emptyObject = require('fbjs/lib/emptyObject');
const UPDATE_SIGNAL = {};
var scheduledAnimationCallback = null;
var scheduledDeferredCallback = null;
type Container = { rootID: string, children: Array<Instance | TextInstance> };
type Props = { prop: any };
type Instance = {| type: string, id: number, children: Array<Instance | TextInstance>, prop: any |};
type TextInstance = {| text: string, id: number |};
var instanceCounter = 0;
var failInBeginPhase = false;
var NoopRenderer = ReactFiberReconciler({
getRootHostContext() {
if (failInBeginPhase) {
throw new Error('Error in host config.');
}
return emptyObject;
},
getChildHostContext() {
return emptyObject;
},
getPublicInstance(instance) {
return instance;
},
createInstance(type : string, props : Props) : Instance {
const inst = {
id: instanceCounter++,
type: type,
children: [],
prop: props.prop,
};
// Hide from unit tests
Object.defineProperty(inst, 'id', { value: inst.id, enumerable: false });
return inst;
},
appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
parentInstance.children.push(child);
},
finalizeInitialChildren(domElement : Instance, type : string, props : Props) : boolean {
return false;
},
prepareUpdate(instance : Instance, type : string, oldProps : Props, newProps : Props) : null | {} {
return UPDATE_SIGNAL;
},
commitMount(instance : Instance, type : string, newProps : Props) : void {
// Noop
},
commitUpdate(instance : Instance, updatePayload : Object, type : string, oldProps : Props, newProps : Props) : void {
instance.prop = newProps.prop;
},
shouldSetTextContent(props : Props) : boolean {
return (
typeof props.children === 'string' ||
typeof props.children === 'number'
);
},
resetTextContent(instance : Instance) : void {},
createTextInstance(
text : string,
rootContainerInstance : Container,
hostContext : Object,
internalInstanceHandle : Object
) : TextInstance {
var inst = { text : text, id: instanceCounter++ };
// Hide from unit tests
Object.defineProperty(inst, 'id', { value: inst.id, enumerable: false });
return inst;
},
commitTextUpdate(textInstance : TextInstance, oldText : string, newText : string) : void {
textInstance.text = newText;
},
appendChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
const index = parentInstance.children.indexOf(child);
if (index !== -1) {
parentInstance.children.splice(index, 1);
}
parentInstance.children.push(child);
},
insertBefore(
parentInstance : Instance | Container,
child : Instance | TextInstance,
beforeChild : Instance | TextInstance
) : void {
const index = parentInstance.children.indexOf(child);
if (index !== -1) {
parentInstance.children.splice(index, 1);
}
const beforeIndex = parentInstance.children.indexOf(beforeChild);
if (beforeIndex === -1) {
throw new Error('This child does not exist.');
}
parentInstance.children.splice(beforeIndex, 0, child);
},
removeChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
const index = parentInstance.children.indexOf(child);
if (index === -1) {
throw new Error('This child does not exist.');
}
parentInstance.children.splice(index, 1);
},
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
scheduleAnimationCallback(callback) {
if (scheduledAnimationCallback) {
throw new Error(
'Scheduling an animation callback twice is excessive. ' +
'Instead, keep track of whether the callback has already been scheduled.'
);
}
scheduledAnimationCallback = callback;
},
scheduleDeferredCallback(callback) {
if (scheduledDeferredCallback) {
throw new Error(
'Scheduling deferred callback twice is excessive. ' +
'Instead, keep track of whether the callback has already been scheduled.'
);
}
scheduledDeferredCallback = callback;
},
prepareForCommit() : void {
},
resetAfterCommit() : void {
},
});
var rootContainers = new Map();
var roots = new Map();
var DEFAULT_ROOT_ID = '<default>';
var ReactNoop = {
getChildren(rootID : string = DEFAULT_ROOT_ID) {
const container = rootContainers.get(rootID);
if (container) {
return container.children;
} else {
return null;
}
},
// Shortcut for testing a single root
render(element : ReactElement<any>, callback: ?Function) {
ReactNoop.renderToRootWithID(element, DEFAULT_ROOT_ID, callback);
},
renderToRootWithID(element : ReactElement<any>, rootID : string, callback : ?Function) {
let root = roots.get(rootID);
if (!root) {
const container = { rootID: rootID, children: [] };
rootContainers.set(rootID, container);
root = NoopRenderer.createContainer(container);
roots.set(rootID, root);
}
NoopRenderer.updateContainer(element, root, null, callback);
},
unmountRootWithID(rootID : string) {
const root = roots.get(rootID);
if (root) {
NoopRenderer.updateContainer(null, root, null, () => {
roots.delete(rootID);
rootContainers.delete(rootID);
});
}
},
findInstance(componentOrElement : Element | ?ReactComponent<any, any, any>) : null | Instance | TextInstance {
if (componentOrElement == null) {
return null;
}
// Unsound duck typing.
const component = (componentOrElement : any);
if (typeof component.id === 'number') {
return component;
}
const inst = ReactInstanceMap.get(component);
return inst ? NoopRenderer.findHostInstance(inst) : null;
},
flushAnimationPri() {
var cb = scheduledAnimationCallback;
if (cb === null) {
return;
}
scheduledAnimationCallback = null;
cb();
},
flushDeferredPri(timeout : number = Infinity) {
var cb = scheduledDeferredCallback;
if (cb === null) {
return;
}
scheduledDeferredCallback = null;
var timeRemaining = timeout;
cb({
timeRemaining() {
// Simulate a fix amount of time progressing between each call.
timeRemaining -= 5;
if (timeRemaining < 0) {
timeRemaining = 0;
}
return timeRemaining;
},
});
},
flush() {
ReactNoop.flushAnimationPri();
ReactNoop.flushDeferredPri();
},
performAnimationWork(fn: Function) {
NoopRenderer.performWithPriority(AnimationPriority, fn);
},
batchedUpdates: NoopRenderer.batchedUpdates,
unbatchedUpdates: NoopRenderer.unbatchedUpdates,
syncUpdates: NoopRenderer.syncUpdates,
// Logs the current state of the tree.
dumpTree(rootID : string = DEFAULT_ROOT_ID) {
const root = roots.get(rootID);
const rootContainer = rootContainers.get(rootID);
if (!root || !rootContainer) {
console.log('Nothing rendered yet.');
return;
}
var bufferedLog = [];
function log(...args) {
bufferedLog.push(...args, '\n');
}
function logHostInstances(children: Array<Instance | TextInstance>, depth) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
var indent = ' '.repeat(depth);
if (typeof child.text === 'string') {
log(indent + '- ' + child.text);
} else {
// $FlowFixMe - The child should've been refined now.
log(indent + '- ' + child.type + '#' + child.id);
// $FlowFixMe - The child should've been refined now.
logHostInstances(child.children, depth + 1);
}
}
}
function logContainer(container : Container, depth) {
log(' '.repeat(depth) + '- [root#' + container.rootID + ']');
logHostInstances(container.children, depth + 1);
}
function logUpdateQueue(updateQueue : UpdateQueue, depth) {
log(
' '.repeat(depth + 1) + 'QUEUED UPDATES'
);
const firstUpdate = updateQueue.first;
if (!firstUpdate) {
return;
}
log(
' '.repeat(depth + 1) + '~',
firstUpdate && firstUpdate.partialState,
firstUpdate.callback ? 'with callback' : '',
'[' + firstUpdate.priorityLevel + ']'
);
var next;
while (next = firstUpdate.next) {
log(
' '.repeat(depth + 1) + '~',
next.partialState,
next.callback ? 'with callback' : '',
'[' + firstUpdate.priorityLevel + ']'
);
}
}
function logFiber(fiber : Fiber, depth) {
log(
' '.repeat(depth) + '- ' + (fiber.type ? fiber.type.name || fiber.type : '[root]'),
'[' + fiber.pendingWorkPriority + (fiber.pendingProps ? '*' : '') + ']'
);
if (fiber.updateQueue) {
logUpdateQueue(fiber.updateQueue, depth);
}
const childInProgress = fiber.progressedChild;
if (childInProgress && childInProgress !== fiber.child) {
log(' '.repeat(depth + 1) + 'IN PROGRESS: ' + fiber.progressedPriority);
logFiber(childInProgress, depth + 1);
if (fiber.child) {
log(' '.repeat(depth + 1) + 'CURRENT');
}
} else if (fiber.child && fiber.updateQueue) {
log(' '.repeat(depth + 1) + 'CHILDREN');
}
if (fiber.child) {
logFiber(fiber.child, depth + 1);
}
if (fiber.sibling) {
logFiber(fiber.sibling, depth);
}
}
log('HOST INSTANCES:');
logContainer(rootContainer, 0);
log('FIBERS:');
logFiber(root.current, 0);
console.log(...bufferedLog);
},
simulateErrorInHostConfig(fn : () => void) {
failInBeginPhase = true;
try {
fn();
} finally {
failInBeginPhase = false;
}
},
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// Private. Used only by fixtures/fiber-debugger.
// (To be fair, it's the only place where `react-noop-renderer` package is used at all.)
ReactFiberInstrumentation,
},
};
module.exports = ReactNoop;

Просмотреть файл

@ -1,170 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDOMFrameScheduling
* @flow
*/
'use strict';
// This a built-in polyfill for requestIdleCallback. It works by scheduling
// a requestAnimationFrame, store the time for the start of the frame, then
// schedule a postMessage which gets scheduled after paint. Within the
// postMessage handler do as much work as possible until time + frame rate.
// By separating the idle call into a separate event tick we ensure that
// layout, paint and other browser work is counted against the available time.
// The frame rate is dynamically adjusted.
import type {Deadline} from 'ReactFiberReconciler';
var invariant = require('fbjs/lib/invariant');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
// TODO: There's no way to cancel these, because Fiber doesn't atm.
let rAF: (callback: (time: number) => void) => number;
let rIC: (callback: (deadline: Deadline) => void) => number;
if (!ExecutionEnvironment.canUseDOM) {
rAF = function(frameCallback: (time: number) => void): number {
setTimeout(frameCallback, 16);
return 0;
};
rIC = function(frameCallback: (deadline: Deadline) => void): number {
setTimeout(() => {
frameCallback({
timeRemaining() {
return Infinity;
},
});
});
return 0;
};
} else if (typeof requestAnimationFrame !== 'function') {
invariant(
false,
'React depends on requestAnimationFrame. Make sure that you load a ' +
'polyfill in older browsers.',
);
} else if (typeof requestIdleCallback !== 'function') {
// Wrap requestAnimationFrame and polyfill requestIdleCallback.
var scheduledRAFCallback = null;
var scheduledRICCallback = null;
var isIdleScheduled = false;
var isAnimationFrameScheduled = false;
var frameDeadline = 0;
// We start out assuming that we run at 30fps but then the heuristic tracking
// will adjust this value to a faster fps if we get more frequent animation
// frames.
var previousFrameTime = 33;
var activeFrameTime = 33;
var frameDeadlineObject = {
timeRemaining: typeof performance === 'object' &&
typeof performance.now === 'function'
? function() {
// We assume that if we have a performance timer that the rAF callback
// gets a performance timer value. Not sure if this is always true.
return frameDeadline - performance.now();
}
: function() {
// As a fallback we use Date.now.
return frameDeadline - Date.now();
},
};
// We use the postMessage trick to defer idle work until after the repaint.
var messageKey = '__reactIdleCallback$' + Math.random().toString(36).slice(2);
var idleTick = function(event) {
if (event.source !== window || event.data !== messageKey) {
return;
}
isIdleScheduled = false;
var callback = scheduledRICCallback;
scheduledRICCallback = null;
if (callback) {
callback(frameDeadlineObject);
}
};
// Assumes that we have addEventListener in this environment. Might need
// something better for old IE.
window.addEventListener('message', idleTick, false);
var animationTick = function(rafTime) {
isAnimationFrameScheduled = false;
var nextFrameTime = rafTime - frameDeadline + activeFrameTime;
if (
nextFrameTime < activeFrameTime &&
previousFrameTime < activeFrameTime
) {
if (nextFrameTime < 8) {
// Defensive coding. We don't support higher frame rates than 120hz.
// If we get lower than that, it is probably a bug.
nextFrameTime = 8;
}
// If one frame goes long, then the next one can be short to catch up.
// If two frames are short in a row, then that's an indication that we
// actually have a higher frame rate than what we're currently optimizing.
// We adjust our heuristic dynamically accordingly. For example, if we're
// running on 120hz display or 90hz VR display.
// Take the max of the two in case one of them was an anomaly due to
// missed frame deadlines.
activeFrameTime = nextFrameTime < previousFrameTime
? previousFrameTime
: nextFrameTime;
} else {
previousFrameTime = nextFrameTime;
}
frameDeadline = rafTime + activeFrameTime;
if (!isIdleScheduled) {
isIdleScheduled = true;
window.postMessage(messageKey, '*');
}
var callback = scheduledRAFCallback;
scheduledRAFCallback = null;
if (callback) {
callback(rafTime);
}
};
rAF = function(callback: (time: number) => void): number {
// This assumes that we only schedule one callback at a time because that's
// how Fiber uses it.
scheduledRAFCallback = callback;
if (!isAnimationFrameScheduled) {
// If rIC didn't already schedule one, we need to schedule a frame.
isAnimationFrameScheduled = true;
requestAnimationFrame(animationTick);
}
return 0;
};
rIC = function(callback: (deadline: Deadline) => void): number {
// This assumes that we only schedule one callback at a time because that's
// how Fiber uses it.
scheduledRICCallback = callback;
if (!isAnimationFrameScheduled) {
// If rAF didn't already schedule one, we need to schedule a frame.
// TODO: If this rAF doesn't materialize because the browser throttles, we
// might want to still have setTimeout trigger rIC as a backup to ensure
// that we keep performing work.
isAnimationFrameScheduled = true;
requestAnimationFrame(animationTick);
}
return 0;
};
} else {
rAF = requestAnimationFrame;
rIC = requestIdleCallback;
}
exports.rAF = rAF;
exports.rIC = rIC;

Просмотреть файл

@ -1,438 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDebugTool
* @flow
*/
'use strict';
var ReactInvalidSetStateWarningHook = require('ReactInvalidSetStateWarningHook');
var ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var {ReactComponentTreeHook} = require('ReactGlobalSharedState');
var performanceNow = require('fbjs/lib/performanceNow');
var warning = require('fbjs/lib/warning');
import type {ReactElement} from 'ReactElementType';
import type {DebugID} from 'ReactInstanceType';
import type {Operation} from 'ReactHostOperationHistoryHook';
type Hook = any;
type TimerType =
| 'ctor'
| 'render'
| 'componentWillMount'
| 'componentWillUnmount'
| 'componentWillReceiveProps'
| 'shouldComponentUpdate'
| 'componentWillUpdate'
| 'componentDidUpdate'
| 'componentDidMount';
type Measurement = {
timerType: TimerType,
instanceID: DebugID,
duration: number,
};
type TreeSnapshot = {
[key: DebugID]: {
displayName: string,
text: string,
updateCount: number,
childIDs: Array<DebugID>,
ownerID: DebugID,
parentID: DebugID,
},
};
type HistoryItem = {
duration: number,
measurements: Array<Measurement>,
operations: Array<Operation>,
treeSnapshot: TreeSnapshot,
};
export type FlushHistory = Array<HistoryItem>;
// Trust the developer to only use this with a __DEV__ check
var ReactDebugTool = ((null: any): typeof ReactDebugTool);
if (__DEV__) {
var hooks = [];
var didHookThrowForEvent = {};
const callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) {
try {
fn.call(context, arg1, arg2, arg3, arg4, arg5);
} catch (e) {
warning(
didHookThrowForEvent[event],
'Exception thrown by hook while handling %s: %s',
event,
e + '\n' + e.stack,
);
didHookThrowForEvent[event] = true;
}
};
const emitEvent = function(event, arg1, arg2, arg3, arg4, arg5) {
for (var i = 0; i < hooks.length; i++) {
var hook = hooks[i];
var fn = hook[event];
if (fn) {
callHook(event, fn, hook, arg1, arg2, arg3, arg4, arg5);
}
}
};
var isProfiling = false;
var flushHistory = [];
var lifeCycleTimerStack = [];
var currentFlushNesting = 0;
var currentFlushMeasurements = [];
var currentFlushStartTime = 0;
var currentTimerDebugID = null;
var currentTimerStartTime = 0;
var currentTimerNestedFlushDuration = 0;
var currentTimerType = null;
var lifeCycleTimerHasWarned = false;
const clearHistory = function() {
ReactComponentTreeHook.purgeUnmountedComponents();
ReactHostOperationHistoryHook.clearHistory();
};
const getTreeSnapshot = function(registeredIDs) {
return registeredIDs.reduce((tree, id) => {
var ownerID = ReactComponentTreeHook.getOwnerID(id);
var parentID = ReactComponentTreeHook.getParentID(id);
tree[id] = {
displayName: ReactComponentTreeHook.getDisplayName(id),
text: ReactComponentTreeHook.getText(id),
updateCount: ReactComponentTreeHook.getUpdateCount(id),
childIDs: ReactComponentTreeHook.getChildIDs(id),
// Text nodes don't have owners but this is close enough.
ownerID: ownerID ||
(parentID && ReactComponentTreeHook.getOwnerID(parentID)) ||
0,
parentID,
};
return tree;
}, {});
};
const resetMeasurements = function() {
var previousStartTime = currentFlushStartTime;
var previousMeasurements = currentFlushMeasurements;
var previousOperations = ReactHostOperationHistoryHook.getHistory();
if (currentFlushNesting === 0) {
currentFlushStartTime = 0;
currentFlushMeasurements = [];
clearHistory();
return;
}
if (previousMeasurements.length || previousOperations.length) {
var registeredIDs = ReactComponentTreeHook.getRegisteredIDs();
flushHistory.push({
duration: performanceNow() - previousStartTime,
measurements: previousMeasurements || [],
operations: previousOperations || [],
treeSnapshot: getTreeSnapshot(registeredIDs),
});
}
clearHistory();
currentFlushStartTime = performanceNow();
currentFlushMeasurements = [];
};
const checkDebugID = function(debugID, allowRoot = false) {
if (allowRoot && debugID === 0) {
return;
}
if (!debugID) {
warning(false, 'ReactDebugTool: debugID may not be empty.');
}
};
const beginLifeCycleTimer = function(debugID, timerType) {
if (currentFlushNesting === 0) {
return;
}
if (currentTimerType && !lifeCycleTimerHasWarned) {
warning(
false,
'There is an internal error in the React performance measurement code.' +
'\n\nDid not expect %s timer to start while %s timer is still in ' +
'progress for %s instance.',
timerType,
currentTimerType || 'no',
debugID === currentTimerDebugID ? 'the same' : 'another',
);
lifeCycleTimerHasWarned = true;
}
currentTimerStartTime = performanceNow();
currentTimerNestedFlushDuration = 0;
currentTimerDebugID = debugID;
currentTimerType = timerType;
};
const endLifeCycleTimer = function(debugID, timerType) {
if (currentFlushNesting === 0) {
return;
}
if (currentTimerType !== timerType && !lifeCycleTimerHasWarned) {
warning(
false,
'There is an internal error in the React performance measurement code. ' +
'We did not expect %s timer to stop while %s timer is still in ' +
'progress for %s instance. Please report this as a bug in React.',
timerType,
currentTimerType || 'no',
debugID === currentTimerDebugID ? 'the same' : 'another',
);
lifeCycleTimerHasWarned = true;
}
if (isProfiling) {
currentFlushMeasurements.push({
timerType,
instanceID: debugID,
duration: performanceNow() -
currentTimerStartTime -
currentTimerNestedFlushDuration,
});
}
currentTimerStartTime = 0;
currentTimerNestedFlushDuration = 0;
currentTimerDebugID = null;
currentTimerType = null;
};
const pauseCurrentLifeCycleTimer = function() {
var currentTimer = {
startTime: currentTimerStartTime,
nestedFlushStartTime: performanceNow(),
debugID: currentTimerDebugID,
timerType: currentTimerType,
};
lifeCycleTimerStack.push(currentTimer);
currentTimerStartTime = 0;
currentTimerNestedFlushDuration = 0;
currentTimerDebugID = null;
currentTimerType = null;
};
const resumeCurrentLifeCycleTimer = function() {
var {
startTime,
nestedFlushStartTime,
debugID,
timerType,
} = lifeCycleTimerStack.pop();
var nestedFlushDuration = performanceNow() - nestedFlushStartTime;
currentTimerStartTime = startTime;
currentTimerNestedFlushDuration += nestedFlushDuration;
currentTimerDebugID = debugID;
currentTimerType = timerType;
};
var lastMarkTimeStamp = 0;
var canUsePerformanceMeasure: boolean =
typeof performance !== 'undefined' &&
typeof performance.mark === 'function' &&
typeof performance.clearMarks === 'function' &&
typeof performance.measure === 'function' &&
typeof performance.clearMeasures === 'function';
const shouldMark = function(debugID) {
if (!isProfiling || !canUsePerformanceMeasure) {
return false;
}
var element = ReactComponentTreeHook.getElement(debugID);
if (element == null || typeof element !== 'object') {
return false;
}
var isHostElement = typeof element.type === 'string';
if (isHostElement) {
return false;
}
return true;
};
const markBegin = function(debugID, markType) {
if (!shouldMark(debugID)) {
return;
}
var markName = `${debugID}::${markType}`;
lastMarkTimeStamp = performanceNow();
performance.mark(markName);
};
const markEnd = function(debugID, markType) {
if (!shouldMark(debugID)) {
return;
}
var markName = `${debugID}::${markType}`;
var displayName =
ReactComponentTreeHook.getDisplayName(debugID) || 'Unknown';
// Chrome has an issue of dropping markers recorded too fast:
// https://bugs.chromium.org/p/chromium/issues/detail?id=640652
// To work around this, we will not report very small measurements.
// I determined the magic number by tweaking it back and forth.
// 0.05ms was enough to prevent the issue, but I set it to 0.1ms to be safe.
// When the bug is fixed, we can `measure()` unconditionally if we want to.
var timeStamp = performanceNow();
if (timeStamp - lastMarkTimeStamp > 0.1) {
var measurementName = `${displayName} [${markType}]`;
performance.measure(measurementName, markName);
}
performance.clearMarks(markName);
if (measurementName) {
performance.clearMeasures(measurementName);
}
};
ReactDebugTool = {
addHook(hook: Hook): void {
hooks.push(hook);
},
removeHook(hook: Hook): void {
for (var i = 0; i < hooks.length; i++) {
if (hooks[i] === hook) {
hooks.splice(i, 1);
i--;
}
}
},
isProfiling(): boolean {
return isProfiling;
},
beginProfiling(): void {
if (isProfiling) {
return;
}
isProfiling = true;
flushHistory.length = 0;
resetMeasurements();
ReactDebugTool.addHook(ReactHostOperationHistoryHook);
},
endProfiling(): void {
if (!isProfiling) {
return;
}
isProfiling = false;
resetMeasurements();
ReactDebugTool.removeHook(ReactHostOperationHistoryHook);
},
getFlushHistory(): FlushHistory {
return flushHistory;
},
onBeginFlush(): void {
currentFlushNesting++;
resetMeasurements();
pauseCurrentLifeCycleTimer();
emitEvent('onBeginFlush');
},
onEndFlush(): void {
resetMeasurements();
currentFlushNesting--;
resumeCurrentLifeCycleTimer();
emitEvent('onEndFlush');
},
onBeginLifeCycleTimer(debugID: DebugID, timerType: TimerType): void {
checkDebugID(debugID);
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
markBegin(debugID, timerType);
beginLifeCycleTimer(debugID, timerType);
},
onEndLifeCycleTimer(debugID: DebugID, timerType: TimerType): void {
checkDebugID(debugID);
endLifeCycleTimer(debugID, timerType);
markEnd(debugID, timerType);
emitEvent('onEndLifeCycleTimer', debugID, timerType);
},
onBeginProcessingChildContext(): void {
emitEvent('onBeginProcessingChildContext');
},
onEndProcessingChildContext(): void {
emitEvent('onEndProcessingChildContext');
},
onHostOperation(operation: Operation) {
checkDebugID(operation.instanceID);
emitEvent('onHostOperation', operation);
},
onSetState(): void {
emitEvent('onSetState');
},
onSetChildren(debugID: DebugID, childDebugIDs: Array<DebugID>) {
checkDebugID(debugID);
childDebugIDs.forEach(checkDebugID);
emitEvent('onSetChildren', debugID, childDebugIDs);
},
onBeforeMountComponent(
debugID: DebugID,
element: ReactElement,
parentDebugID: DebugID,
): void {
checkDebugID(debugID);
checkDebugID(parentDebugID, true);
emitEvent('onBeforeMountComponent', debugID, element, parentDebugID);
markBegin(debugID, 'mount');
},
onMountComponent(debugID: DebugID): void {
checkDebugID(debugID);
markEnd(debugID, 'mount');
emitEvent('onMountComponent', debugID);
},
onBeforeUpdateComponent(debugID: DebugID, element: ReactElement): void {
checkDebugID(debugID);
emitEvent('onBeforeUpdateComponent', debugID, element);
markBegin(debugID, 'update');
},
onUpdateComponent(debugID: DebugID): void {
checkDebugID(debugID);
markEnd(debugID, 'update');
emitEvent('onUpdateComponent', debugID);
},
onBeforeUnmountComponent(debugID: DebugID): void {
checkDebugID(debugID);
emitEvent('onBeforeUnmountComponent', debugID);
markBegin(debugID, 'unmount');
},
onUnmountComponent(debugID: DebugID): void {
checkDebugID(debugID);
markEnd(debugID, 'unmount');
emitEvent('onUnmountComponent', debugID);
},
onTestEvent(): void {
emitEvent('onTestEvent');
},
};
ReactDebugTool.addHook(ReactInvalidSetStateWarningHook);
ReactDebugTool.addHook(ReactComponentTreeHook);
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if (/[?&]react_perf\b/.test(url)) {
ReactDebugTool.beginProfiling();
}
}
module.exports = ReactDebugTool;

Просмотреть файл

@ -1,28 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactGlobalSharedState
*/
'use strict';
var ReactInternals = require('react')
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var ReactGlobalSharedState = {
ReactCurrentOwner: ReactInternals.ReactCurrentOwner,
};
if (__DEV__) {
Object.assign(ReactGlobalSharedState, {
ReactComponentTreeHook: ReactInternals.ReactComponentTreeHook,
ReactDebugCurrentFrame: ReactInternals.ReactDebugCurrentFrame,
});
}
module.exports = ReactGlobalSharedState;

Просмотреть файл

@ -1,23 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactInstrumentation
* @flow
*/
'use strict';
// Trust the developer to only use ReactInstrumentation with a __DEV__ check
var debugTool = ((null: any): typeof ReactDebugTool);
if (__DEV__) {
var ReactDebugTool = require('ReactDebugTool');
debugTool = ReactDebugTool;
}
module.exports = {debugTool};

Просмотреть файл

@ -1,458 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPerf
* @flow
*/
'use strict';
var ReactDebugTool = require('ReactDebugTool');
var warning = require('fbjs/lib/warning');
var alreadyWarned = false;
import type {FlushHistory} from 'ReactDebugTool';
function roundFloat(val, base = 2) {
var n = Math.pow(10, base);
return Math.floor(val * n) / n;
}
// Flow type definition of console.table is too strict right now, see
// https://github.com/facebook/flow/pull/2353 for updates
function consoleTable(table: Array<{[key: string]: any}>): void {
console.table((table: any));
}
function warnInProduction() {
if (alreadyWarned) {
return;
}
alreadyWarned = true;
if (typeof console !== 'undefined') {
console.error(
'ReactPerf is not supported in the production builds of React. ' +
'To collect measurements, please use the development build of React instead.',
);
}
}
function getLastMeasurements() {
if (!__DEV__) {
warnInProduction();
return [];
}
return ReactDebugTool.getFlushHistory();
}
function getExclusive(flushHistory = getLastMeasurements()) {
if (!__DEV__) {
warnInProduction();
return [];
}
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(
treeSnapshot,
instanceID,
timerType,
applyUpdate,
) {
var {displayName} = treeSnapshot[instanceID];
var key = displayName;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
counts: {},
durations: {},
totalDuration: 0,
};
}
if (!stats.durations[timerType]) {
stats.durations[timerType] = 0;
}
if (!stats.counts[timerType]) {
stats.counts[timerType] = 0;
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
flushHistory.forEach(flush => {
var {measurements, treeSnapshot} = flush;
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
updateAggregatedStats(treeSnapshot, instanceID, timerType, stats => {
stats.totalDuration += duration;
stats.durations[timerType] += duration;
stats.counts[timerType]++;
});
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.totalDuration - a.totalDuration);
}
function getInclusive(flushHistory = getLastMeasurements()) {
if (!__DEV__) {
warnInProduction();
return [];
}
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
inclusiveRenderDuration: 0,
renderCount: 0,
};
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
var isCompositeByID = {};
flushHistory.forEach(flush => {
var {measurements} = flush;
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
isCompositeByID[instanceID] = true;
});
});
flushHistory.forEach(flush => {
var {measurements, treeSnapshot} = flush;
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.renderCount++;
});
var nextParentID = instanceID;
while (nextParentID) {
// As we traverse parents, only count inclusive time towards composites.
// We know something is a composite if its render() was called.
if (isCompositeByID[nextParentID]) {
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
}
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
}
function getWasted(flushHistory = getLastMeasurements()) {
if (!__DEV__) {
warnInProduction();
return [];
}
var aggregatedStats = {};
var affectedIDs = {};
function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
var stats = aggregatedStats[key];
if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
key,
instanceCount: 0,
inclusiveRenderDuration: 0,
renderCount: 0,
};
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
flushHistory.forEach(flush => {
var {measurements, treeSnapshot, operations} = flush;
var isDefinitelyNotWastedByID = {};
// Find host components associated with an operation in this batch.
// Mark all components in their parent tree as definitely not wasted.
operations.forEach(operation => {
var {instanceID} = operation;
var nextParentID = instanceID;
while (nextParentID) {
isDefinitelyNotWastedByID[nextParentID] = true;
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
// Find composite components that rendered in this batch.
// These are potential candidates for being wasted renders.
var renderedCompositeIDs = {};
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
renderedCompositeIDs[instanceID] = true;
});
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
// If there was a DOM update below this component, or it has just been
// mounted, its render() is not considered wasted.
var {updateCount} = treeSnapshot[instanceID];
if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) {
return;
}
// We consider this render() wasted.
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.renderCount++;
});
var nextParentID = instanceID;
while (nextParentID) {
// Any parents rendered during this batch are considered wasted
// unless we previously marked them as dirty.
var isWasted =
renderedCompositeIDs[nextParentID] &&
!isDefinitelyNotWastedByID[nextParentID];
if (isWasted) {
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
}
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
});
return Object.keys(aggregatedStats)
.map(key => ({
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
}
function getOperations(flushHistory = getLastMeasurements()) {
if (!__DEV__) {
warnInProduction();
return [];
}
var stats = [];
flushHistory.forEach((flush, flushIndex) => {
var {operations, treeSnapshot} = flush;
operations.forEach(operation => {
var {instanceID, type, payload} = operation;
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
stats.push({
flushIndex,
instanceID,
key,
type,
ownerID,
payload,
});
});
});
return stats;
}
function printExclusive(flushHistory?: FlushHistory) {
if (!__DEV__) {
warnInProduction();
return;
}
var stats = getExclusive(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, totalDuration} = item;
var renderCount = item.counts.render || 0;
var renderDuration = item.durations.render || 0;
return {
Component: key,
'Total time (ms)': roundFloat(totalDuration),
'Instance count': instanceCount,
'Total render time (ms)': roundFloat(renderDuration),
'Average render time (ms)': renderCount
? roundFloat(renderDuration / renderCount)
: undefined,
'Render count': renderCount,
'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration),
};
});
consoleTable(table);
}
function printInclusive(flushHistory?: FlushHistory) {
if (!__DEV__) {
warnInProduction();
return;
}
var stats = getInclusive(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
return {
'Owner > Component': key,
'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration),
'Instance count': instanceCount,
'Render count': renderCount,
};
});
consoleTable(table);
}
function printWasted(flushHistory?: FlushHistory) {
if (!__DEV__) {
warnInProduction();
return;
}
var stats = getWasted(flushHistory);
var table = stats.map(item => {
var {key, instanceCount, inclusiveRenderDuration, renderCount} = item;
return {
'Owner > Component': key,
'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration),
'Instance count': instanceCount,
'Render count': renderCount,
};
});
consoleTable(table);
}
function printOperations(flushHistory?: FlushHistory) {
if (!__DEV__) {
warnInProduction();
return;
}
var stats = getOperations(flushHistory);
var table = stats.map(stat => ({
'Owner > Node': stat.key,
Operation: stat.type,
Payload: typeof stat.payload === 'object'
? JSON.stringify(stat.payload)
: stat.payload,
'Flush index': stat.flushIndex,
'Owner Component ID': stat.ownerID,
'DOM Component ID': stat.instanceID,
}));
consoleTable(table);
}
var warnedAboutPrintDOM = false;
function printDOM(measurements: FlushHistory) {
warning(
warnedAboutPrintDOM,
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.',
);
warnedAboutPrintDOM = true;
return printOperations(measurements);
}
var warnedAboutGetMeasurementsSummaryMap = false;
function getMeasurementsSummaryMap(measurements: FlushHistory) {
warning(
warnedAboutGetMeasurementsSummaryMap,
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.',
);
warnedAboutGetMeasurementsSummaryMap = true;
return getWasted(measurements);
}
function start() {
if (!__DEV__) {
warnInProduction();
return;
}
ReactDebugTool.beginProfiling();
}
function stop() {
if (!__DEV__) {
warnInProduction();
return;
}
ReactDebugTool.endProfiling();
}
function isRunning() {
if (!__DEV__) {
warnInProduction();
return false;
}
return ReactDebugTool.isProfiling();
}
var ReactPerfAnalysis = {
getLastMeasurements,
getExclusive,
getInclusive,
getWasted,
getOperations,
printExclusive,
printInclusive,
printWasted,
printOperations,
start,
stop,
isRunning,
// Deprecated:
printDOM,
getMeasurementsSummaryMap,
};
module.exports = ReactPerfAnalysis;

Просмотреть файл

@ -1,55 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
const describeFiber = ReactDOMFeatureFlags.useFiber ? describe : xdescribe;
describeFiber('ReactDOMFrameScheduling', () => {
it('throws when requestAnimationFrame is not polyfilled in the browser', () => {
const previousRAF = global.requestAnimationFrame;
try {
global.requestAnimationFrame = undefined;
jest.resetModules();
expect(() => {
require('ReactDOM');
}).toThrow(
'React depends on requestAnimationFrame. Make sure that you load a ' +
'polyfill in older browsers.',
);
} finally {
global.requestAnimationFrame = previousRAF;
}
});
// We're just testing importing, not using it.
// It is important because even isomorphic components may import it.
it('can import findDOMNode in Node environment', () => {
const previousRAF = global.requestAnimationFrame;
const previousRIC = global.requestIdleCallback;
const prevWindow = global.window;
try {
global.requestAnimationFrame = undefined;
global.requestIdleCallback = undefined;
// Simulate the Node environment:
delete global.window;
jest.resetModules();
expect(() => {
require('ReactDOM');
}).not.toThrow();
} finally {
global.requestAnimationFrame = previousRAF;
global.requestIdleCallback = previousRIC;
global.window = prevWindow;
}
});
});

Просмотреть файл

@ -1,84 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
describe('ReactDebugTool', () => {
var ReactDebugTool;
beforeEach(() => {
jest.resetModules();
ReactDebugTool = require('ReactDebugTool');
});
it('should add and remove hooks', () => {
var handler1 = jasmine.createSpy('spy');
var handler2 = jasmine.createSpy('spy');
var hook1 = {onTestEvent: handler1};
var hook2 = {onTestEvent: handler2};
ReactDebugTool.addHook(hook1);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(1);
expect(handler2.calls.count()).toBe(0);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(2);
expect(handler2.calls.count()).toBe(0);
ReactDebugTool.addHook(hook2);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(3);
expect(handler2.calls.count()).toBe(1);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(4);
expect(handler2.calls.count()).toBe(2);
ReactDebugTool.removeHook(hook1);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(4);
expect(handler2.calls.count()).toBe(3);
ReactDebugTool.removeHook(hook2);
ReactDebugTool.onTestEvent();
expect(handler1.calls.count()).toBe(4);
expect(handler2.calls.count()).toBe(3);
});
it('warns once when an error is thrown in hook', () => {
spyOn(console, 'error');
ReactDebugTool.addHook({
onTestEvent() {
throw new Error('Hi.');
},
});
ReactDebugTool.onTestEvent();
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Exception thrown by hook while handling ' + 'onTestEvent: Error: Hi.',
);
ReactDebugTool.onTestEvent();
expectDev(console.error.calls.count()).toBe(1);
});
it('returns isProfiling state', () => {
expect(ReactDebugTool.isProfiling()).toBe(false);
ReactDebugTool.beginProfiling();
expect(ReactDebugTool.isProfiling()).toBe(true);
ReactDebugTool.endProfiling();
expect(ReactDebugTool.isProfiling()).toBe(false);
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,60 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDebugCurrentFiber
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
type LifeCyclePhase = 'render' | 'getChildContext';
if (__DEV__) {
var getComponentName = require('getComponentName');
var {
getStackAddendumByWorkInProgressFiber,
} = require('ReactFiberComponentTreeHook');
}
function getCurrentFiberOwnerName(): string | null {
if (__DEV__) {
const fiber = ReactDebugCurrentFiber.current;
if (fiber === null) {
return null;
}
if (fiber._debugOwner != null) {
return getComponentName(fiber._debugOwner);
}
}
return null;
}
function getCurrentFiberStackAddendum(): string | null {
if (__DEV__) {
const fiber = ReactDebugCurrentFiber.current;
if (fiber === null) {
return null;
}
// Safe because if current fiber exists, we are reconciling,
// and it is guaranteed to be the work-in-progress version.
return getStackAddendumByWorkInProgressFiber(fiber);
}
return null;
}
var ReactDebugCurrentFiber = {
current: (null: Fiber | null),
phase: (null: LifeCyclePhase | null),
getCurrentFiberOwnerName,
getCurrentFiberStackAddendum,
};
module.exports = ReactDebugCurrentFiber;

Просмотреть файл

@ -1,401 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDebugFiberPerf
* @flow
*/
import type {Fiber} from 'ReactFiber';
type MeasurementPhase =
| 'componentWillMount'
| 'componentWillUnmount'
| 'componentWillReceiveProps'
| 'shouldComponentUpdate'
| 'componentWillUpdate'
| 'componentDidUpdate'
| 'componentDidMount'
| 'getChildContext';
// Trust the developer to only use this with a __DEV__ check
let ReactDebugFiberPerf = ((null: any): typeof ReactDebugFiberPerf);
if (__DEV__) {
const {
HostRoot,
HostComponent,
HostText,
HostPortal,
YieldComponent,
Fragment,
} = require('ReactTypeOfWork');
const getComponentName = require('getComponentName');
// Prefix measurements so that it's possible to filter them.
// Longer prefixes are hard to read in DevTools.
const reactEmoji = '\u269B';
const warningEmoji = '\u26D4';
const supportsUserTiming =
typeof performance !== 'undefined' &&
typeof performance.mark === 'function' &&
typeof performance.clearMarks === 'function' &&
typeof performance.measure === 'function' &&
typeof performance.clearMeasures === 'function';
// Keep track of current fiber so that we know the path to unwind on pause.
// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
let currentFiber: Fiber | null = null;
// If we're in the middle of user code, which fiber and method is it?
// Reusing `currentFiber` would be confusing for this because user code fiber
// can change during commit phase too, but we don't need to unwind it (since
// lifecycles in the commit phase don't resemble a tree).
let currentPhase: MeasurementPhase | null = null;
let currentPhaseFiber: Fiber | null = null;
// Did lifecycle hook schedule an update? This is often a performance problem,
// so we will keep track of it, and include it in the report.
// Track commits caused by cascading updates.
let isCommitting: boolean = false;
let hasScheduledUpdateInCurrentCommit: boolean = false;
let hasScheduledUpdateInCurrentPhase: boolean = false;
let commitCountInCurrentWorkLoop: number = 0;
let effectCountInCurrentCommit: number = 0;
// During commits, we only show a measurement once per method name
// to avoid stretch the commit phase with measurement overhead.
const labelsInCurrentCommit: Set<string> = new Set();
const formatMarkName = (markName: string) => {
return `${reactEmoji} ${markName}`;
};
const formatLabel = (label: string, warning: string | null) => {
const prefix = warning ? `${warningEmoji} ` : `${reactEmoji} `;
const suffix = warning ? ` Warning: ${warning}` : '';
return `${prefix}${label}${suffix}`;
};
const beginMark = (markName: string) => {
performance.mark(formatMarkName(markName));
};
const clearMark = (markName: string) => {
performance.clearMarks(formatMarkName(markName));
};
const endMark = (label: string, markName: string, warning: string | null) => {
const formattedMarkName = formatMarkName(markName);
const formattedLabel = formatLabel(label, warning);
try {
performance.measure(formattedLabel, formattedMarkName);
} catch (err) {
// If previous mark was missing for some reason, this will throw.
// This could only happen if React crashed in an unexpected place earlier.
// Don't pile on with more errors.
}
// Clear marks immediately to avoid growing buffer.
performance.clearMarks(formattedMarkName);
performance.clearMeasures(formattedLabel);
};
const getFiberMarkName = (label: string, debugID: number) => {
return `${label} (#${debugID})`;
};
const getFiberLabel = (
componentName: string,
isMounted: boolean,
phase: MeasurementPhase | null,
) => {
if (phase === null) {
// These are composite component total time measurements.
return `${componentName} [${isMounted ? 'update' : 'mount'}]`;
} else {
// Composite component methods.
return `${componentName}.${phase}`;
}
};
const beginFiberMark = (
fiber: Fiber,
phase: MeasurementPhase | null,
): boolean => {
const componentName = getComponentName(fiber) || 'Unknown';
const debugID = ((fiber._debugID: any): number);
const isMounted = fiber.alternate !== null;
const label = getFiberLabel(componentName, isMounted, phase);
if (isCommitting && labelsInCurrentCommit.has(label)) {
// During the commit phase, we don't show duplicate labels because
// there is a fixed overhead for every measurement, and we don't
// want to stretch the commit phase beyond necessary.
return false;
}
labelsInCurrentCommit.add(label);
const markName = getFiberMarkName(label, debugID);
beginMark(markName);
return true;
};
const clearFiberMark = (fiber: Fiber, phase: MeasurementPhase | null) => {
const componentName = getComponentName(fiber) || 'Unknown';
const debugID = ((fiber._debugID: any): number);
const isMounted = fiber.alternate !== null;
const label = getFiberLabel(componentName, isMounted, phase);
const markName = getFiberMarkName(label, debugID);
clearMark(markName);
};
const endFiberMark = (
fiber: Fiber,
phase: MeasurementPhase | null,
warning: string | null,
) => {
const componentName = getComponentName(fiber) || 'Unknown';
const debugID = ((fiber._debugID: any): number);
const isMounted = fiber.alternate !== null;
const label = getFiberLabel(componentName, isMounted, phase);
const markName = getFiberMarkName(label, debugID);
endMark(label, markName, warning);
};
const shouldIgnoreFiber = (fiber: Fiber): boolean => {
// Host components should be skipped in the timeline.
// We could check typeof fiber.type, but does this work with RN?
switch (fiber.tag) {
case HostRoot:
case HostComponent:
case HostText:
case HostPortal:
case YieldComponent:
case Fragment:
return true;
default:
return false;
}
};
const clearPendingPhaseMeasurement = () => {
if (currentPhase !== null && currentPhaseFiber !== null) {
clearFiberMark(currentPhaseFiber, currentPhase);
}
currentPhaseFiber = null;
currentPhase = null;
hasScheduledUpdateInCurrentPhase = false;
};
const pauseTimers = () => {
// Stops all currently active measurements so that they can be resumed
// if we continue in a later deferred loop from the same unit of work.
let fiber = currentFiber;
while (fiber) {
if (fiber._debugIsCurrentlyTiming) {
endFiberMark(fiber, null, null);
}
fiber = fiber.return;
}
};
const resumeTimersRecursively = (fiber: Fiber) => {
if (fiber.return !== null) {
resumeTimersRecursively(fiber.return);
}
if (fiber._debugIsCurrentlyTiming) {
beginFiberMark(fiber, null);
}
};
const resumeTimers = () => {
// Resumes all measurements that were active during the last deferred loop.
if (currentFiber !== null) {
resumeTimersRecursively(currentFiber);
}
};
ReactDebugFiberPerf = {
recordEffect(): void {
effectCountInCurrentCommit++;
},
recordScheduleUpdate(): void {
if (isCommitting) {
hasScheduledUpdateInCurrentCommit = true;
}
if (
currentPhase !== null &&
currentPhase !== 'componentWillMount' &&
currentPhase !== 'componentWillReceiveProps'
) {
hasScheduledUpdateInCurrentPhase = true;
}
},
startWorkTimer(fiber: Fiber): void {
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
return;
}
// If we pause, this is the fiber to unwind from.
currentFiber = fiber;
if (!beginFiberMark(fiber, null)) {
return;
}
fiber._debugIsCurrentlyTiming = true;
},
cancelWorkTimer(fiber: Fiber): void {
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
return;
}
// Remember we shouldn't complete measurement for this fiber.
// Otherwise flamechart will be deep even for small updates.
fiber._debugIsCurrentlyTiming = false;
clearFiberMark(fiber, null);
},
stopWorkTimer(fiber: Fiber): void {
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
return;
}
// If we pause, its parent is the fiber to unwind from.
currentFiber = fiber.return;
if (!fiber._debugIsCurrentlyTiming) {
return;
}
fiber._debugIsCurrentlyTiming = false;
endFiberMark(fiber, null, null);
},
startPhaseTimer(fiber: Fiber, phase: MeasurementPhase): void {
if (!supportsUserTiming) {
return;
}
clearPendingPhaseMeasurement();
if (!beginFiberMark(fiber, phase)) {
return;
}
currentPhaseFiber = fiber;
currentPhase = phase;
},
stopPhaseTimer(): void {
if (!supportsUserTiming) {
return;
}
if (currentPhase !== null && currentPhaseFiber !== null) {
const warning = hasScheduledUpdateInCurrentPhase
? 'Scheduled a cascading update'
: null;
endFiberMark(currentPhaseFiber, currentPhase, warning);
}
currentPhase = null;
currentPhaseFiber = null;
},
startWorkLoopTimer(): void {
if (!supportsUserTiming) {
return;
}
commitCountInCurrentWorkLoop = 0;
// This is top level call.
// Any other measurements are performed within.
beginMark('(React Tree Reconciliation)');
// Resume any measurements that were in progress during the last loop.
resumeTimers();
},
stopWorkLoopTimer(): void {
if (!supportsUserTiming) {
return;
}
const warning = commitCountInCurrentWorkLoop > 1
? 'There were cascading updates'
: null;
commitCountInCurrentWorkLoop = 0;
// Pause any measurements until the next loop.
pauseTimers();
endMark(
'(React Tree Reconciliation)',
'(React Tree Reconciliation)',
warning,
);
},
startCommitTimer(): void {
if (!supportsUserTiming) {
return;
}
isCommitting = true;
hasScheduledUpdateInCurrentCommit = false;
labelsInCurrentCommit.clear();
beginMark('(Committing Changes)');
},
stopCommitTimer(): void {
if (!supportsUserTiming) {
return;
}
let warning = null;
if (hasScheduledUpdateInCurrentCommit) {
warning = 'Lifecycle hook scheduled a cascading update';
} else if (commitCountInCurrentWorkLoop > 0) {
warning = 'Caused by a cascading update in earlier commit';
}
hasScheduledUpdateInCurrentCommit = false;
commitCountInCurrentWorkLoop++;
isCommitting = false;
labelsInCurrentCommit.clear();
endMark('(Committing Changes)', '(Committing Changes)', warning);
},
startCommitHostEffectsTimer(): void {
if (!supportsUserTiming) {
return;
}
effectCountInCurrentCommit = 0;
beginMark('(Committing Host Effects)');
},
stopCommitHostEffectsTimer(): void {
if (!supportsUserTiming) {
return;
}
const count = effectCountInCurrentCommit;
effectCountInCurrentCommit = 0;
endMark(
`(Committing Host Effects: ${count} Total)`,
'(Committing Host Effects)',
null,
);
},
startCommitLifeCyclesTimer(): void {
if (!supportsUserTiming) {
return;
}
effectCountInCurrentCommit = 0;
beginMark('(Calling Lifecycle Methods)');
},
stopCommitLifeCyclesTimer(): void {
if (!supportsUserTiming) {
return;
}
const count = effectCountInCurrentCommit;
effectCountInCurrentCommit = 0;
endMark(
`(Calling Lifecycle Methods: ${count} Total)`,
'(Calling Lifecycle Methods)',
null,
);
},
};
}
module.exports = ReactDebugFiberPerf;

Просмотреть файл

@ -1,474 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiber
* @flow
*/
'use strict';
import type {ReactElement, Source} from 'ReactElementType';
import type {ReactInstance, DebugID} from 'ReactInstanceType';
import type {ReactFragment} from 'ReactTypes';
import type {ReactCoroutine, ReactYield} from 'ReactCoroutine';
import type {ReactPortal} from 'ReactPortal';
import type {TypeOfWork} from 'ReactTypeOfWork';
import type {TypeOfInternalContext} from 'ReactTypeOfInternalContext';
import type {TypeOfSideEffect} from 'ReactTypeOfSideEffect';
import type {PriorityLevel} from 'ReactPriorityLevel';
import type {UpdateQueue} from 'ReactFiberUpdateQueue';
var {
IndeterminateComponent,
ClassComponent,
HostRoot,
HostComponent,
HostText,
HostPortal,
CoroutineComponent,
YieldComponent,
Fragment,
} = require('ReactTypeOfWork');
var {NoWork} = require('ReactPriorityLevel');
var {NoContext} = require('ReactTypeOfInternalContext');
var {NoEffect} = require('ReactTypeOfSideEffect');
var {cloneUpdateQueue} = require('ReactFiberUpdateQueue');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var getComponentName = require('getComponentName');
var hasBadMapPolyfill = false;
try {
const nonExtensibleObject = Object.preventExtensions({});
/* eslint-disable no-new */
new Map([[nonExtensibleObject, null]]);
new Set([nonExtensibleObject]);
/* eslint-enable no-new */
} catch (e) {
// TODO: Consider warning about bad polyfills
hasBadMapPolyfill = true;
}
}
// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
export type Fiber = {
// __DEV__ only
_debugID?: DebugID,
_debugSource?: Source | null,
_debugOwner?: Fiber | ReactInstance | null, // Stack compatible
_debugIsCurrentlyTiming?: boolean,
// These first fields are conceptually members of an Instance. This used to
// be split into a separate type and intersected with the other Fiber fields,
// but until Flow fixes its intersection bugs, we've merged them into a
// single type.
// An Instance is shared between all versions of a component. We can easily
// break this out into a separate object to avoid copying so much to the
// alternate versions of the tree. We put this on a single object for now to
// minimize the number of objects created during the initial render.
// Tag identifying the type of fiber.
tag: TypeOfWork,
// Unique identifier of this child.
key: null | string,
// The function/class/module associated with this fiber.
type: any,
// The local state associated with this fiber.
stateNode: any,
// Conceptual aliases
// parent : Instance -> return The parent happens to be the same as the
// return fiber since we've merged the fiber and instance.
// Remaining fields belong to Fiber
// The Fiber to return to after finishing processing this one.
// This is effectively the parent, but there can be multiple parents (two)
// so this is only the parent of the thing we're currently processing.
// It is conceptually the same as the return address of a stack frame.
return: Fiber | null,
// Singly Linked List Tree Structure.
child: Fiber | null,
sibling: Fiber | null,
index: number,
// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
ref: null | (((handle: mixed) => void) & {_stringRef: ?string}),
// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any, // This type will be more specific once we overload the tag.
// TODO: I think that there is a way to merge pendingProps and memoizedProps.
memoizedProps: any, // The props used to create the output.
// A queue of state updates and callbacks.
updateQueue: UpdateQueue | null,
// The state used to create the output
memoizedState: any,
// Bitfield that describes properties about the fiber and its subtree. E.g.
// the AsyncUpdates flag indicates whether the subtree should be async-by-
// default. When a fiber is created, it inherits the internalContextTag of its
// parent. Additional flags can be set at creation time, but after than the
// value should remain unchanged throughout the fiber's lifetime, particularly
// before its child fibers are created.
internalContextTag: TypeOfInternalContext,
// Effect
effectTag: TypeOfSideEffect,
// Singly linked list fast path to the next fiber with side-effects.
nextEffect: Fiber | null,
// The first and last fiber with side-effect within this subtree. This allows
// us to reuse a slice of the linked list when we reuse the work done within
// this fiber.
firstEffect: Fiber | null,
lastEffect: Fiber | null,
// This will be used to quickly determine if a subtree has no pending changes.
pendingWorkPriority: PriorityLevel,
// This value represents the priority level that was last used to process this
// component. This indicates whether it is better to continue from the
// progressed work or if it is better to continue from the current state.
progressedPriority: PriorityLevel,
// If work bails out on a Fiber that already had some work started at a lower
// priority, then we need to store the progressed work somewhere. This holds
// the started child set until we need to get back to working on it. It may
// or may not be the same as the "current" child.
progressedChild: Fiber | null,
// When we reconcile children onto progressedChild it is possible that we have
// to delete some child fibers. We need to keep track of this side-effects so
// that if we continue later on, we have to include those effects. Deletions
// are added in the reverse order from sibling pointers.
progressedFirstDeletion: Fiber | null,
progressedLastDeletion: Fiber | null,
// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
alternate: Fiber | null,
// Conceptual aliases
// workInProgress : Fiber -> alternate The alternate used for reuse happens
// to be the same as work in progress.
};
if (__DEV__) {
var debugCounter = 1;
}
// This is a constructor of a POJO instead of a constructor function for a few
// reasons:
// 1) Nobody should add any instance methods on this. Instance methods can be
// more difficult to predict when they get optimized and they are almost
// never inlined properly in static compilers.
// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
// always know when it is a fiber.
// 3) We can easily go from a createFiber call to calling a constructor if that
// is faster. The opposite is not true.
// 4) We might want to experiment with using numeric keys since they are easier
// to optimize in a non-JIT environment.
// 5) It should be easy to port this to a C struct and keep a C implementation
// compatible.
var createFiber = function(
tag: TypeOfWork,
key: null | string,
internalContextTag: TypeOfInternalContext,
): Fiber {
var fiber: Fiber = {
// Instance
tag: tag,
key: key,
type: null,
stateNode: null,
// Fiber
return: null,
child: null,
sibling: null,
index: 0,
ref: null,
pendingProps: null,
memoizedProps: null,
updateQueue: null,
memoizedState: null,
internalContextTag,
effectTag: NoEffect,
nextEffect: null,
firstEffect: null,
lastEffect: null,
pendingWorkPriority: NoWork,
progressedPriority: NoWork,
progressedChild: null,
progressedFirstDeletion: null,
progressedLastDeletion: null,
alternate: null,
};
if (__DEV__) {
fiber._debugID = debugCounter++;
fiber._debugSource = null;
fiber._debugOwner = null;
fiber._debugIsCurrentlyTiming = false;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(fiber);
}
}
return fiber;
};
function shouldConstruct(Component) {
return !!(Component.prototype && Component.prototype.isReactComponent);
}
// This is used to create an alternate fiber to do work on.
// TODO: Rename to createWorkInProgressFiber or something like that.
exports.cloneFiber = function(
fiber: Fiber,
priorityLevel: PriorityLevel,
): Fiber {
// We clone to get a work in progress. That means that this fiber is the
// current. To make it safe to reuse that fiber later on as work in progress
// we need to reset its work in progress flag now. We don't have an
// opportunity to do this earlier since we don't traverse the tree when
// the work in progress tree becomes the current tree.
// fiber.progressedPriority = NoWork;
// fiber.progressedChild = null;
// We use a double buffering pooling technique because we know that we'll only
// ever need at most two versions of a tree. We pool the "other" unused node
// that we're free to reuse. This is lazily created to avoid allocating extra
// objects for things that are never updated. It also allow us to reclaim the
// extra memory if needed.
let alt = fiber.alternate;
if (alt !== null) {
// If we clone, then we do so from the "current" state. The current state
// can't have any side-effects that are still valid so we reset just to be
// sure.
alt.effectTag = NoEffect;
alt.nextEffect = null;
alt.firstEffect = null;
alt.lastEffect = null;
} else {
// This should not have an alternate already
alt = createFiber(fiber.tag, fiber.key, fiber.internalContextTag);
alt.type = fiber.type;
alt.progressedChild = fiber.progressedChild;
alt.progressedPriority = fiber.progressedPriority;
alt.alternate = fiber;
fiber.alternate = alt;
}
alt.stateNode = fiber.stateNode;
alt.child = fiber.child;
alt.sibling = fiber.sibling; // This should always be overridden. TODO: null
alt.index = fiber.index; // This should always be overridden.
alt.ref = fiber.ref;
// pendingProps is here for symmetry but is unnecessary in practice for now.
// TODO: Pass in the new pendingProps as an argument maybe?
alt.pendingProps = fiber.pendingProps;
cloneUpdateQueue(fiber, alt);
alt.pendingWorkPriority = priorityLevel;
alt.memoizedProps = fiber.memoizedProps;
alt.memoizedState = fiber.memoizedState;
if (__DEV__) {
alt._debugID = fiber._debugID;
alt._debugSource = fiber._debugSource;
alt._debugOwner = fiber._debugOwner;
}
return alt;
};
exports.createHostRootFiber = function(): Fiber {
const fiber = createFiber(HostRoot, null, NoContext);
return fiber;
};
exports.createFiberFromElement = function(
element: ReactElement,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
let owner = null;
if (__DEV__) {
owner = element._owner;
}
const fiber = createFiberFromElementType(
element.type,
element.key,
internalContextTag,
owner,
);
fiber.pendingProps = element.props;
fiber.pendingWorkPriority = priorityLevel;
if (__DEV__) {
fiber._debugSource = element._source;
fiber._debugOwner = element._owner;
}
return fiber;
};
exports.createFiberFromFragment = function(
elements: ReactFragment,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
// TODO: Consider supporting keyed fragments. Technically, we accidentally
// support that in the existing React.
const fiber = createFiber(Fragment, null, internalContextTag);
fiber.pendingProps = elements;
fiber.pendingWorkPriority = priorityLevel;
return fiber;
};
exports.createFiberFromText = function(
content: string,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
const fiber = createFiber(HostText, null, internalContextTag);
fiber.pendingProps = content;
fiber.pendingWorkPriority = priorityLevel;
return fiber;
};
function createFiberFromElementType(
type: mixed,
key: null | string,
internalContextTag: TypeOfInternalContext,
debugOwner: null | Fiber | ReactInstance,
): Fiber {
let fiber;
if (typeof type === 'function') {
fiber = shouldConstruct(type)
? createFiber(ClassComponent, key, internalContextTag)
: createFiber(IndeterminateComponent, key, internalContextTag);
fiber.type = type;
} else if (typeof type === 'string') {
fiber = createFiber(HostComponent, key, internalContextTag);
fiber.type = type;
} else if (
typeof type === 'object' &&
type !== null &&
typeof type.tag === 'number'
) {
// Currently assumed to be a continuation and therefore is a fiber already.
// TODO: The yield system is currently broken for updates in some cases.
// The reified yield stores a fiber, but we don't know which fiber that is;
// the current or a workInProgress? When the continuation gets rendered here
// we don't know if we can reuse that fiber or if we need to clone it.
// There is probably a clever way to restructure this.
fiber = ((type: any): Fiber);
} else {
let info = '';
if (__DEV__) {
if (
type === undefined ||
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
"it's defined in.";
}
const ownerName = debugOwner ? getComponentName(debugOwner) : null;
if (ownerName) {
info += '\n\nCheck the render method of `' + ownerName + '`.';
}
}
invariant(
false,
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
type == null ? type : typeof type,
info,
);
}
return fiber;
}
exports.createFiberFromElementType = createFiberFromElementType;
exports.createFiberFromCoroutine = function(
coroutine: ReactCoroutine,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
const fiber = createFiber(
CoroutineComponent,
coroutine.key,
internalContextTag,
);
fiber.type = coroutine.handler;
fiber.pendingProps = coroutine;
fiber.pendingWorkPriority = priorityLevel;
return fiber;
};
exports.createFiberFromYield = function(
yieldNode: ReactYield,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
const fiber = createFiber(YieldComponent, null, internalContextTag);
return fiber;
};
exports.createFiberFromPortal = function(
portal: ReactPortal,
internalContextTag: TypeOfInternalContext,
priorityLevel: PriorityLevel,
): Fiber {
const fiber = createFiber(HostPortal, portal.key, internalContextTag);
fiber.pendingProps = portal.children || [];
fiber.pendingWorkPriority = priorityLevel;
fiber.stateNode = {
containerInfo: portal.containerInfo,
implementation: portal.implementation,
};
return fiber;
};

Просмотреть файл

@ -1,871 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberBeginWork
* @flow
*/
'use strict';
import type {ReactCoroutine} from 'ReactCoroutine';
import type {Fiber} from 'ReactFiber';
import type {HostContext} from 'ReactFiberHostContext';
import type {FiberRoot} from 'ReactFiberRoot';
import type {HostConfig} from 'ReactFiberReconciler';
import type {PriorityLevel} from 'ReactPriorityLevel';
var {
mountChildFibersInPlace,
reconcileChildFibers,
reconcileChildFibersInPlace,
cloneChildFibers,
} = require('ReactChildFiber');
var {beginUpdateQueue} = require('ReactFiberUpdateQueue');
var ReactTypeOfWork = require('ReactTypeOfWork');
var {
getMaskedContext,
getUnmaskedContext,
hasContextChanged,
pushContextProvider,
pushTopLevelContextObject,
invalidateContextProvider,
} = require('ReactFiberContext');
var {
IndeterminateComponent,
FunctionalComponent,
ClassComponent,
HostRoot,
HostComponent,
HostText,
HostPortal,
CoroutineComponent,
CoroutineHandlerPhase,
YieldComponent,
Fragment,
} = ReactTypeOfWork;
var {NoWork, OffscreenPriority} = require('ReactPriorityLevel');
var {Placement, ContentReset, Err, Ref} = require('ReactTypeOfSideEffect');
var ReactFiberClassComponent = require('ReactFiberClassComponent');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
var {cancelWorkTimer} = require('ReactDebugFiberPerf');
var warning = require('fbjs/lib/warning');
var warnedAboutStatelessRefs = {};
}
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
hostContext: HostContext<C, CX>,
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
) {
const {
shouldSetTextContent,
useSyncScheduling,
shouldDeprioritizeSubtree,
} = config;
const {pushHostContext, pushHostContainer} = hostContext;
const {
adoptClassInstance,
constructClassInstance,
mountClassInstance,
resumeMountClassInstance,
updateClassInstance,
} = ReactFiberClassComponent(
scheduleUpdate,
getPriorityContext,
memoizeProps,
memoizeState,
);
function markChildAsProgressed(current, workInProgress, priorityLevel) {
// We now have clones. Let's store them as the currently progressed work.
workInProgress.progressedChild = workInProgress.child;
workInProgress.progressedPriority = priorityLevel;
if (current !== null) {
// We also store it on the current. When the alternate swaps in we can
// continue from this point.
current.progressedChild = workInProgress.progressedChild;
current.progressedPriority = workInProgress.progressedPriority;
}
}
function clearDeletions(workInProgress) {
workInProgress.progressedFirstDeletion = workInProgress.progressedLastDeletion = null;
}
function transferDeletions(workInProgress) {
// Any deletions get added first into the effect list.
workInProgress.firstEffect = workInProgress.progressedFirstDeletion;
workInProgress.lastEffect = workInProgress.progressedLastDeletion;
}
function reconcileChildren(current, workInProgress, nextChildren) {
const priorityLevel = workInProgress.pendingWorkPriority;
reconcileChildrenAtPriority(
current,
workInProgress,
nextChildren,
priorityLevel,
);
}
function reconcileChildrenAtPriority(
current,
workInProgress,
nextChildren,
priorityLevel,
) {
// At this point any memoization is no longer valid since we'll have changed
// the children.
workInProgress.memoizedProps = null;
if (current === null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
// we will add them all to the child before it gets rendered. That means
// we can optimize this reconciliation pass by not tracking side-effects.
workInProgress.child = mountChildFibersInPlace(
workInProgress,
workInProgress.child,
nextChildren,
priorityLevel,
);
} else if (current.child === workInProgress.child) {
// If the current child is the same as the work in progress, it means that
// we haven't yet started any work on these children. Therefore, we use
// the clone algorithm to create a copy of all the current children.
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
clearDeletions(workInProgress);
workInProgress.child = reconcileChildFibers(
workInProgress,
workInProgress.child,
nextChildren,
priorityLevel,
);
transferDeletions(workInProgress);
} else {
// If, on the other hand, it is already using a clone, that means we've
// already begun some work on this tree and we can continue where we left
// off by reconciling against the existing children.
workInProgress.child = reconcileChildFibersInPlace(
workInProgress,
workInProgress.child,
nextChildren,
priorityLevel,
);
transferDeletions(workInProgress);
}
markChildAsProgressed(current, workInProgress, priorityLevel);
}
function updateFragment(current, workInProgress) {
var nextChildren = workInProgress.pendingProps;
if (hasContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
if (nextChildren === null) {
nextChildren = workInProgress.memoizedProps;
}
} else if (
nextChildren === null ||
workInProgress.memoizedProps === nextChildren
) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
reconcileChildren(current, workInProgress, nextChildren);
memoizeProps(workInProgress, nextChildren);
return workInProgress.child;
}
function markRef(current: Fiber | null, workInProgress: Fiber) {
const ref = workInProgress.ref;
if (ref !== null && (!current || current.ref !== ref)) {
// Schedule a Ref effect
workInProgress.effectTag |= Ref;
}
}
function updateFunctionalComponent(current, workInProgress) {
var fn = workInProgress.type;
var nextProps = workInProgress.pendingProps;
const memoizedProps = workInProgress.memoizedProps;
if (hasContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
if (nextProps === null) {
nextProps = memoizedProps;
}
} else {
if (nextProps === null || memoizedProps === nextProps) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
// TODO: Disable this before release, since it is not part of the public API
// I use this for testing to compare the relative overhead of classes.
if (
typeof fn.shouldComponentUpdate === 'function' &&
!fn.shouldComponentUpdate(memoizedProps, nextProps)
) {
// Memoize props even if shouldComponentUpdate returns false
memoizeProps(workInProgress, nextProps);
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
}
var unmaskedContext = getUnmaskedContext(workInProgress);
var context = getMaskedContext(workInProgress, unmaskedContext);
var nextChildren;
if (__DEV__) {
ReactCurrentOwner.current = workInProgress;
ReactDebugCurrentFiber.phase = 'render';
nextChildren = fn(nextProps, context);
ReactDebugCurrentFiber.phase = null;
} else {
nextChildren = fn(nextProps, context);
}
reconcileChildren(current, workInProgress, nextChildren);
memoizeProps(workInProgress, nextProps);
return workInProgress.child;
}
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
priorityLevel: PriorityLevel,
) {
// Push context providers early to prevent context stack mismatches.
// During mounting we don't know the child context yet as the instance doesn't exist.
// We will invalidate the child context in finishClassComponent() right after rendering.
const hasContext = pushContextProvider(workInProgress);
let shouldUpdate;
if (current === null) {
if (!workInProgress.stateNode) {
// In the initial pass we might need to construct the instance.
constructClassInstance(workInProgress, workInProgress.pendingProps);
mountClassInstance(workInProgress, priorityLevel);
shouldUpdate = true;
} else {
// In a resume, we'll already have an instance we can reuse.
shouldUpdate = resumeMountClassInstance(workInProgress, priorityLevel);
}
} else {
shouldUpdate = updateClassInstance(
current,
workInProgress,
priorityLevel,
);
}
return finishClassComponent(
current,
workInProgress,
shouldUpdate,
hasContext,
);
}
function finishClassComponent(
current: Fiber | null,
workInProgress: Fiber,
shouldUpdate: boolean,
hasContext: boolean,
) {
// Refs should update even if shouldComponentUpdate returns false
markRef(current, workInProgress);
if (!shouldUpdate) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
const instance = workInProgress.stateNode;
// Rerender
ReactCurrentOwner.current = workInProgress;
let nextChildren;
if (__DEV__) {
ReactDebugCurrentFiber.phase = 'render';
nextChildren = instance.render();
ReactDebugCurrentFiber.phase = null;
} else {
nextChildren = instance.render();
}
reconcileChildren(current, workInProgress, nextChildren);
// Memoize props and state using the values we just used to render.
// TODO: Restructure so we never read values from the instance.
memoizeState(workInProgress, instance.state);
memoizeProps(workInProgress, instance.props);
// The context might have changed so we need to recalculate it.
if (hasContext) {
invalidateContextProvider(workInProgress);
}
return workInProgress.child;
}
function updateHostRoot(current, workInProgress, priorityLevel) {
const root = (workInProgress.stateNode: FiberRoot);
if (root.pendingContext) {
pushTopLevelContextObject(
workInProgress,
root.pendingContext,
root.pendingContext !== root.context,
);
} else if (root.context) {
// Should always be set
pushTopLevelContextObject(workInProgress, root.context, false);
}
pushHostContainer(workInProgress, root.containerInfo);
const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
const prevState = workInProgress.memoizedState;
const state = beginUpdateQueue(
workInProgress,
updateQueue,
null,
prevState,
null,
priorityLevel,
);
if (prevState === state) {
// If the state is the same as before, that's a bailout because we had
// no work matching this priority.
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
const element = state.element;
reconcileChildren(current, workInProgress, element);
memoizeState(workInProgress, state);
return workInProgress.child;
}
// If there is no update queue, that's a bailout because the root has no props.
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
function updateHostComponent(current, workInProgress) {
pushHostContext(workInProgress);
let nextProps = workInProgress.pendingProps;
const prevProps = current !== null ? current.memoizedProps : null;
const memoizedProps = workInProgress.memoizedProps;
if (hasContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
if (nextProps === null) {
nextProps = memoizedProps;
invariant(
nextProps !== null,
'We should always have pending or current props. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
} else if (nextProps === null || memoizedProps === nextProps) {
if (
!useSyncScheduling &&
shouldDeprioritizeSubtree(workInProgress.type, memoizedProps) &&
workInProgress.pendingWorkPriority !== OffscreenPriority
) {
// This subtree still has work, but it should be deprioritized so we need
// to bail out and not do any work yet.
// TODO: It would be better if this tree got its correct priority set
// during scheduleUpdate instead because otherwise we'll start a higher
// priority reconciliation first before we can get down here. However,
// that is a bit tricky since workInProgress and current can have
// different "hidden" settings.
let child = workInProgress.progressedChild;
while (child !== null) {
// To ensure that this subtree gets its priority reset, the children
// need to be reset.
child.pendingWorkPriority = OffscreenPriority;
child = child.sibling;
}
return null;
}
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
let nextChildren = nextProps.children;
const isDirectTextChild = shouldSetTextContent(nextProps);
if (isDirectTextChild) {
// We special case a direct text child of a host node. This is a common
// case. We won't handle it as a reified child. We will instead handle
// this in the host environment that also have access to this prop. That
// avoids allocating another HostText fiber and traversing it.
nextChildren = null;
} else if (prevProps && shouldSetTextContent(prevProps)) {
// If we're switching from a direct text child to a normal child, or to
// empty, we need to schedule the text content to be reset.
workInProgress.effectTag |= ContentReset;
}
markRef(current, workInProgress);
if (
!useSyncScheduling &&
shouldDeprioritizeSubtree(workInProgress.type, nextProps) &&
workInProgress.pendingWorkPriority !== OffscreenPriority
) {
// If this host component is hidden, we can bail out on the children.
// We'll rerender the children later at the lower priority.
// It is unfortunate that we have to do the reconciliation of these
// children already since that will add them to the tree even though
// they are not actually done yet. If this is a large set it is also
// confusing that this takes time to do right now instead of later.
if (workInProgress.progressedPriority === OffscreenPriority) {
// If we already made some progress on the offscreen priority before,
// then we should continue from where we left off.
workInProgress.child = workInProgress.progressedChild;
}
// Reconcile the children and stash them for later work.
reconcileChildrenAtPriority(
current,
workInProgress,
nextChildren,
OffscreenPriority,
);
memoizeProps(workInProgress, nextProps);
workInProgress.child = current !== null ? current.child : null;
if (current === null) {
// If this doesn't have a current we won't track it for placement
// effects. However, when we come back around to this we have already
// inserted the parent which means that we'll infact need to make this a
// placement.
// TODO: There has to be a better solution to this problem.
let child = workInProgress.progressedChild;
while (child !== null) {
child.effectTag = Placement;
child = child.sibling;
}
}
// Abort and don't process children yet.
return null;
} else {
reconcileChildren(current, workInProgress, nextChildren);
memoizeProps(workInProgress, nextProps);
return workInProgress.child;
}
}
function updateHostText(current, workInProgress) {
let nextProps = workInProgress.pendingProps;
if (nextProps === null) {
nextProps = workInProgress.memoizedProps;
}
memoizeProps(workInProgress, nextProps);
// Nothing to do here. This is terminal. We'll do the completion step
// immediately after.
return null;
}
function mountIndeterminateComponent(current, workInProgress, priorityLevel) {
invariant(
current === null,
'An indeterminate component should never have mounted. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
var fn = workInProgress.type;
var props = workInProgress.pendingProps;
var unmaskedContext = getUnmaskedContext(workInProgress);
var context = getMaskedContext(workInProgress, unmaskedContext);
var value;
if (__DEV__) {
ReactCurrentOwner.current = workInProgress;
value = fn(props, context);
} else {
value = fn(props, context);
}
if (
typeof value === 'object' &&
value !== null &&
typeof value.render === 'function'
) {
// Proceed under the assumption that this is a class instance
workInProgress.tag = ClassComponent;
// Push context providers early to prevent context stack mismatches.
// During mounting we don't know the child context yet as the instance doesn't exist.
// We will invalidate the child context in finishClassComponent() right after rendering.
const hasContext = pushContextProvider(workInProgress);
adoptClassInstance(workInProgress, value);
mountClassInstance(workInProgress, priorityLevel);
return finishClassComponent(current, workInProgress, true, hasContext);
} else {
// Proceed under the assumption that this is a functional component
workInProgress.tag = FunctionalComponent;
if (__DEV__) {
const Component = workInProgress.type;
if (Component) {
warning(
!Component.childContextTypes,
'%s(...): childContextTypes cannot be defined on a functional component.',
Component.displayName || Component.name || 'Component',
);
}
if (workInProgress.ref !== null) {
let info = '';
const ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName();
if (ownerName) {
info += '\n\nCheck the render method of `' + ownerName + '`.';
}
let warningKey = ownerName || workInProgress._debugID || '';
const debugSource = workInProgress._debugSource;
if (debugSource) {
warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
}
if (!warnedAboutStatelessRefs[warningKey]) {
warnedAboutStatelessRefs[warningKey] = true;
warning(
false,
'Stateless function components cannot be given refs. ' +
'Attempts to access this ref will fail.%s%s',
info,
ReactDebugCurrentFiber.getCurrentFiberStackAddendum(),
);
}
}
}
reconcileChildren(current, workInProgress, value);
memoizeProps(workInProgress, props);
return workInProgress.child;
}
}
function updateCoroutineComponent(current, workInProgress) {
var nextCoroutine = (workInProgress.pendingProps: null | ReactCoroutine);
if (hasContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
if (nextCoroutine === null) {
nextCoroutine = current && current.memoizedProps;
invariant(
nextCoroutine !== null,
'We should always have pending or current props. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
} else if (
nextCoroutine === null ||
workInProgress.memoizedProps === nextCoroutine
) {
nextCoroutine = workInProgress.memoizedProps;
// TODO: When bailing out, we might need to return the stateNode instead
// of the child. To check it for work.
// return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
const nextChildren = nextCoroutine.children;
const priorityLevel = workInProgress.pendingWorkPriority;
// The following is a fork of reconcileChildrenAtPriority but using
// stateNode to store the child.
// At this point any memoization is no longer valid since we'll have changed
// the children.
workInProgress.memoizedProps = null;
if (current === null) {
workInProgress.stateNode = mountChildFibersInPlace(
workInProgress,
workInProgress.stateNode,
nextChildren,
priorityLevel,
);
} else if (current.child === workInProgress.child) {
clearDeletions(workInProgress);
workInProgress.stateNode = reconcileChildFibers(
workInProgress,
workInProgress.stateNode,
nextChildren,
priorityLevel,
);
transferDeletions(workInProgress);
} else {
workInProgress.stateNode = reconcileChildFibersInPlace(
workInProgress,
workInProgress.stateNode,
nextChildren,
priorityLevel,
);
transferDeletions(workInProgress);
}
memoizeProps(workInProgress, nextCoroutine);
// This doesn't take arbitrary time so we could synchronously just begin
// eagerly do the work of workInProgress.child as an optimization.
return workInProgress.stateNode;
}
function updatePortalComponent(current, workInProgress) {
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
const priorityLevel = workInProgress.pendingWorkPriority;
let nextChildren = workInProgress.pendingProps;
if (hasContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
if (nextChildren === null) {
nextChildren = current && current.memoizedProps;
invariant(
nextChildren != null,
'We should always have pending or current props. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
} else if (
nextChildren === null ||
workInProgress.memoizedProps === nextChildren
) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
if (current === null) {
// Portals are special because we don't append the children during mount
// but at commit. Therefore we need to track insertions which the normal
// flow doesn't do during mount. This doesn't happen at the root because
// the root always starts with a "current" with a null child.
// TODO: Consider unifying this with how the root works.
workInProgress.child = reconcileChildFibersInPlace(
workInProgress,
workInProgress.child,
nextChildren,
priorityLevel,
);
memoizeProps(workInProgress, nextChildren);
markChildAsProgressed(current, workInProgress, priorityLevel);
} else {
reconcileChildren(current, workInProgress, nextChildren);
memoizeProps(workInProgress, nextChildren);
}
return workInProgress.child;
}
/*
function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
let child = firstChild;
do {
// Ensure that the first and last effect of the parent corresponds
// to the children's first and last effect.
if (!returnFiber.firstEffect) {
returnFiber.firstEffect = child.firstEffect;
}
if (child.lastEffect) {
if (returnFiber.lastEffect) {
returnFiber.lastEffect.nextEffect = child.firstEffect;
}
returnFiber.lastEffect = child.lastEffect;
}
} while (child = child.sibling);
}
*/
function bailoutOnAlreadyFinishedWork(
current,
workInProgress: Fiber,
): Fiber | null {
if (__DEV__) {
cancelWorkTimer(workInProgress);
}
const priorityLevel = workInProgress.pendingWorkPriority;
// TODO: We should ideally be able to bail out early if the children have no
// more work to do. However, since we don't have a separation of this
// Fiber's priority and its children yet - we don't know without doing lots
// of the same work we do anyway. Once we have that separation we can just
// bail out here if the children has no more work at this priority level.
// if (workInProgress.priorityOfChildren <= priorityLevel) {
// // If there are side-effects in these children that have not yet been
// // committed we need to ensure that they get properly transferred up.
// if (current && current.child !== workInProgress.child) {
// reuseChildrenEffects(workInProgress, child);
// }
// return null;
// }
if (current && workInProgress.child === current.child) {
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
clearDeletions(workInProgress);
}
cloneChildFibers(current, workInProgress);
markChildAsProgressed(current, workInProgress, priorityLevel);
return workInProgress.child;
}
function bailoutOnLowPriority(current, workInProgress) {
if (__DEV__) {
cancelWorkTimer(workInProgress);
}
// TODO: Handle HostComponent tags here as well and call pushHostContext()?
// See PR 8590 discussion for context
switch (workInProgress.tag) {
case ClassComponent:
pushContextProvider(workInProgress);
break;
case HostPortal:
pushHostContainer(
workInProgress,
workInProgress.stateNode.containerInfo,
);
break;
}
// TODO: What if this is currently in progress?
// How can that happen? How is this not being cloned?
return null;
}
function memoizeProps(workInProgress: Fiber, nextProps: any) {
workInProgress.memoizedProps = nextProps;
// Reset the pending props
workInProgress.pendingProps = null;
}
function memoizeState(workInProgress: Fiber, nextState: any) {
workInProgress.memoizedState = nextState;
// Don't reset the updateQueue, in case there are pending updates. Resetting
// is handled by beginUpdateQueue.
}
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
priorityLevel: PriorityLevel,
): Fiber | null {
if (
workInProgress.pendingWorkPriority === NoWork ||
workInProgress.pendingWorkPriority > priorityLevel
) {
return bailoutOnLowPriority(current, workInProgress);
}
if (__DEV__) {
ReactDebugCurrentFiber.current = workInProgress;
}
// If we don't bail out, we're going be recomputing our children so we need
// to drop our effect list.
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
if (workInProgress.progressedPriority === priorityLevel) {
// If we have progressed work on this priority level already, we can
// proceed this that as the child.
workInProgress.child = workInProgress.progressedChild;
}
switch (workInProgress.tag) {
case IndeterminateComponent:
return mountIndeterminateComponent(
current,
workInProgress,
priorityLevel,
);
case FunctionalComponent:
return updateFunctionalComponent(current, workInProgress);
case ClassComponent:
return updateClassComponent(current, workInProgress, priorityLevel);
case HostRoot:
return updateHostRoot(current, workInProgress, priorityLevel);
case HostComponent:
return updateHostComponent(current, workInProgress);
case HostText:
return updateHostText(current, workInProgress);
case CoroutineHandlerPhase:
// This is a restart. Reset the tag to the initial phase.
workInProgress.tag = CoroutineComponent;
// Intentionally fall through since this is now the same.
case CoroutineComponent:
return updateCoroutineComponent(current, workInProgress);
case YieldComponent:
// A yield component is just a placeholder, we can just run through the
// next one immediately.
return null;
case HostPortal:
return updatePortalComponent(current, workInProgress);
case Fragment:
return updateFragment(current, workInProgress);
default:
invariant(
false,
'Unknown unit of work tag. This error is likely caused by a bug in ' +
'React. Please file an issue.',
);
}
}
function beginFailedWork(
current: Fiber | null,
workInProgress: Fiber,
priorityLevel: PriorityLevel,
) {
invariant(
workInProgress.tag === ClassComponent || workInProgress.tag === HostRoot,
'Invalid type of work. This error is likely caused by a bug in React. ' +
'Please file an issue.',
);
// Add an error effect so we can handle the error during the commit phase
workInProgress.effectTag |= Err;
if (
workInProgress.pendingWorkPriority === NoWork ||
workInProgress.pendingWorkPriority > priorityLevel
) {
return bailoutOnLowPriority(current, workInProgress);
}
// If we don't bail out, we're going be recomputing our children so we need
// to drop our effect list.
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
// Unmount the current children as if the component rendered null
const nextChildren = null;
reconcileChildren(current, workInProgress, nextChildren);
if (workInProgress.tag === ClassComponent) {
const instance = workInProgress.stateNode;
workInProgress.memoizedProps = instance.props;
workInProgress.memoizedState = instance.state;
workInProgress.pendingProps = null;
}
return workInProgress.child;
}
return {
beginWork,
beginFailedWork,
};
};

Просмотреть файл

@ -1,597 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberClassComponent
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {PriorityLevel} from 'ReactPriorityLevel';
var {Update} = require('ReactTypeOfSideEffect');
var ReactFeatureFlags = require('ReactFeatureFlags');
var {AsyncUpdates} = require('ReactTypeOfInternalContext');
var {
cacheContext,
getMaskedContext,
getUnmaskedContext,
isContextConsumer,
} = require('ReactFiberContext');
var {
addUpdate,
addReplaceUpdate,
addForceUpdate,
beginUpdateQueue,
} = require('ReactFiberUpdateQueue');
var {hasContextChanged} = require('ReactFiberContext');
var {isMounted} = require('ReactFiberTreeReflection');
var ReactInstanceMap = require('ReactInstanceMap');
var emptyObject = require('fbjs/lib/emptyObject');
var getComponentName = require('getComponentName');
var shallowEqual = require('fbjs/lib/shallowEqual');
var invariant = require('fbjs/lib/invariant');
const isArray = Array.isArray;
if (__DEV__) {
var {startPhaseTimer, stopPhaseTimer} = require('ReactDebugFiberPerf');
var warning = require('fbjs/lib/warning');
var warnOnInvalidCallback = function(callback: mixed, callerName: string) {
warning(
callback === null || typeof callback === 'function',
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
callback,
);
};
}
module.exports = function(
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
memoizeProps: (workInProgress: Fiber, props: any) => void,
memoizeState: (workInProgress: Fiber, state: any) => void,
) {
// Class component state updater
const updater = {
isMounted,
enqueueSetState(instance, partialState, callback) {
const fiber = ReactInstanceMap.get(instance);
const priorityLevel = getPriorityContext(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
addUpdate(fiber, partialState, callback, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
},
enqueueReplaceState(instance, state, callback) {
const fiber = ReactInstanceMap.get(instance);
const priorityLevel = getPriorityContext(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'replaceState');
}
addReplaceUpdate(fiber, state, callback, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
},
enqueueForceUpdate(instance, callback) {
const fiber = ReactInstanceMap.get(instance);
const priorityLevel = getPriorityContext(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
addForceUpdate(fiber, callback, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
},
};
function checkShouldComponentUpdate(
workInProgress,
oldProps,
newProps,
oldState,
newState,
newContext,
) {
if (
oldProps === null ||
(workInProgress.updateQueue !== null &&
workInProgress.updateQueue.hasForceUpdate)
) {
// If the workInProgress already has an Update effect, return true
return true;
}
const instance = workInProgress.stateNode;
const type = workInProgress.type;
if (typeof instance.shouldComponentUpdate === 'function') {
if (__DEV__) {
startPhaseTimer(workInProgress, 'shouldComponentUpdate');
}
const shouldUpdate = instance.shouldComponentUpdate(
newProps,
newState,
newContext,
);
if (__DEV__) {
stopPhaseTimer();
}
if (__DEV__) {
warning(
shouldUpdate !== undefined,
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
'boolean value. Make sure to return true or false.',
getComponentName(workInProgress) || 'Unknown',
);
}
return shouldUpdate;
}
if (type.prototype && type.prototype.isPureReactComponent) {
return (
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
);
}
return true;
}
function checkClassInstance(workInProgress: Fiber) {
const instance = workInProgress.stateNode;
const type = workInProgress.type;
if (__DEV__) {
const name = getComponentName(workInProgress);
const renderPresent = instance.render;
warning(
renderPresent,
'%s(...): No `render` method found on the returned component ' +
'instance: you may have forgotten to define `render`.',
name,
);
const noGetInitialStateOnES6 =
!instance.getInitialState ||
instance.getInitialState.isReactClassApproved ||
instance.state;
warning(
noGetInitialStateOnES6,
'getInitialState was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Did you mean to define a state property instead?',
name,
);
const noGetDefaultPropsOnES6 =
!instance.getDefaultProps ||
instance.getDefaultProps.isReactClassApproved;
warning(
noGetDefaultPropsOnES6,
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Use a static property to define defaultProps instead.',
name,
);
const noInstancePropTypes = !instance.propTypes;
warning(
noInstancePropTypes,
'propTypes was defined as an instance property on %s. Use a static ' +
'property to define propTypes instead.',
name,
);
const noInstanceContextTypes = !instance.contextTypes;
warning(
noInstanceContextTypes,
'contextTypes was defined as an instance property on %s. Use a static ' +
'property to define contextTypes instead.',
name,
);
const noComponentShouldUpdate =
typeof instance.componentShouldUpdate !== 'function';
warning(
noComponentShouldUpdate,
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
name,
);
if (
type.prototype &&
type.prototype.isPureReactComponent &&
typeof instance.shouldComponentUpdate !== 'undefined'
) {
warning(
false,
'%s has a method called shouldComponentUpdate(). ' +
'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
'Please extend React.Component if shouldComponentUpdate is used.',
getComponentName(workInProgress) || 'A pure component',
);
}
const noComponentDidUnmount =
typeof instance.componentDidUnmount !== 'function';
warning(
noComponentDidUnmount,
'%s has a method called ' +
'componentDidUnmount(). But there is no such lifecycle method. ' +
'Did you mean componentWillUnmount()?',
name,
);
const noComponentWillRecieveProps =
typeof instance.componentWillRecieveProps !== 'function';
warning(
noComponentWillRecieveProps,
'%s has a method called ' +
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
name,
);
const hasMutatedProps = instance.props !== workInProgress.pendingProps;
warning(
instance.props === undefined || !hasMutatedProps,
'%s(...): When calling super() in `%s`, make sure to pass ' +
"up the same props that your component's constructor was passed.",
name,
name,
);
const noInstanceDefaultProps = !instance.defaultProps;
warning(
noInstanceDefaultProps,
'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
' Instead, define defaultProps as a static property on %s.',
name,
name,
);
}
const state = instance.state;
if (state && (typeof state !== 'object' || isArray(state))) {
invariant(
false,
'%s.state: must be set to an object or null',
getComponentName(workInProgress),
);
}
if (typeof instance.getChildContext === 'function') {
invariant(
typeof workInProgress.type.childContextTypes === 'object',
'%s.getChildContext(): childContextTypes must be defined in order to ' +
'use getChildContext().',
getComponentName(workInProgress),
);
}
}
function resetInputPointers(workInProgress: Fiber, instance: any) {
instance.props = workInProgress.memoizedProps;
instance.state = workInProgress.memoizedState;
}
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = updater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
ReactInstanceMap.set(instance, workInProgress);
}
function constructClassInstance(workInProgress: Fiber, props: any): any {
const ctor = workInProgress.type;
const unmaskedContext = getUnmaskedContext(workInProgress);
const needsContext = isContextConsumer(workInProgress);
const context = needsContext
? getMaskedContext(workInProgress, unmaskedContext)
: emptyObject;
const instance = new ctor(props, context);
adoptClassInstance(workInProgress, instance);
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// ReactFiberContext usually updates this cache but can't for newly-created instances.
if (needsContext) {
cacheContext(workInProgress, unmaskedContext, context);
}
return instance;
}
// Invokes the mount life-cycles on a previously never rendered instance.
function mountClassInstance(
workInProgress: Fiber,
priorityLevel: PriorityLevel,
): void {
if (__DEV__) {
checkClassInstance(workInProgress);
}
const instance = workInProgress.stateNode;
const state = instance.state || null;
let props = workInProgress.pendingProps;
invariant(
props,
'There must be pending props for an initial mount. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
const unmaskedContext = getUnmaskedContext(workInProgress);
instance.props = props;
instance.state = state;
instance.refs = emptyObject;
instance.context = getMaskedContext(workInProgress, unmaskedContext);
if (
ReactFeatureFlags.enableAsyncSubtreeAPI &&
workInProgress.type != null &&
workInProgress.type.unstable_asyncUpdates === true
) {
workInProgress.internalContextTag |= AsyncUpdates;
}
if (typeof instance.componentWillMount === 'function') {
if (__DEV__) {
startPhaseTimer(workInProgress, 'componentWillMount');
}
instance.componentWillMount();
if (__DEV__) {
stopPhaseTimer();
}
// If we had additional state updates during this life-cycle, let's
// process them now.
const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
instance.state = beginUpdateQueue(
workInProgress,
updateQueue,
instance,
state,
props,
priorityLevel,
);
}
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
}
// Called on a preexisting class instance. Returns false if a resumed render
// could be reused.
function resumeMountClassInstance(
workInProgress: Fiber,
priorityLevel: PriorityLevel,
): boolean {
const instance = workInProgress.stateNode;
resetInputPointers(workInProgress, instance);
let newState = workInProgress.memoizedState;
let newProps = workInProgress.pendingProps;
if (!newProps) {
// If there isn't any new props, then we'll reuse the memoized props.
// This could be from already completed work.
newProps = workInProgress.memoizedProps;
invariant(
newProps != null,
'There should always be pending or memoized props. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
const newUnmaskedContext = getUnmaskedContext(workInProgress);
const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
// TODO: Should we deal with a setState that happened after the last
// componentWillMount and before this componentWillMount? Probably
// unsupported anyway.
if (
!checkShouldComponentUpdate(
workInProgress,
workInProgress.memoizedProps,
newProps,
workInProgress.memoizedState,
newState,
newContext,
)
) {
// Update the existing instance's state, props, and context pointers even
// though we're bailing out.
instance.props = newProps;
instance.state = newState;
instance.context = newContext;
return false;
}
// If we didn't bail out we need to construct a new instance. We don't
// want to reuse one that failed to fully mount.
const newInstance = constructClassInstance(workInProgress, newProps);
newInstance.props = newProps;
newInstance.state = newState = newInstance.state || null;
newInstance.context = newContext;
if (typeof newInstance.componentWillMount === 'function') {
if (__DEV__) {
startPhaseTimer(workInProgress, 'componentWillMount');
}
newInstance.componentWillMount();
if (__DEV__) {
stopPhaseTimer();
}
}
// If we had additional state updates, process them now.
// They may be from componentWillMount() or from error boundary's setState()
// during initial mounting.
const newUpdateQueue = workInProgress.updateQueue;
if (newUpdateQueue !== null) {
newInstance.state = beginUpdateQueue(
workInProgress,
newUpdateQueue,
newInstance,
newState,
newProps,
priorityLevel,
);
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
return true;
}
// Invokes the update life-cycles and returns false if it shouldn't rerender.
function updateClassInstance(
current: Fiber,
workInProgress: Fiber,
priorityLevel: PriorityLevel,
): boolean {
const instance = workInProgress.stateNode;
resetInputPointers(workInProgress, instance);
const oldProps = workInProgress.memoizedProps;
let newProps = workInProgress.pendingProps;
if (!newProps) {
// If there aren't any new props, then we'll reuse the memoized props.
// This could be from already completed work.
newProps = oldProps;
invariant(
newProps != null,
'There should always be pending or memoized props. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
const oldContext = instance.context;
const newUnmaskedContext = getUnmaskedContext(workInProgress);
const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
// Note: During these life-cycles, instance.props/instance.state are what
// ever the previously attempted to render - not the "current". However,
// during componentDidUpdate we pass the "current" props.
if (oldProps !== newProps || oldContext !== newContext) {
if (typeof instance.componentWillReceiveProps === 'function') {
if (__DEV__) {
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
}
instance.componentWillReceiveProps(newProps, newContext);
if (__DEV__) {
stopPhaseTimer();
}
if (instance.state !== workInProgress.memoizedState) {
if (__DEV__) {
warning(
false,
'%s.componentWillReceiveProps(): Assigning directly to ' +
"this.state is deprecated (except inside a component's " +
'constructor). Use setState instead.',
getComponentName(workInProgress),
);
}
updater.enqueueReplaceState(instance, instance.state, null);
}
}
}
// Compute the next state using the memoized state and the update queue.
const updateQueue = workInProgress.updateQueue;
const oldState = workInProgress.memoizedState;
// TODO: Previous state can be null.
let newState;
if (updateQueue !== null) {
newState = beginUpdateQueue(
workInProgress,
updateQueue,
instance,
oldState,
newProps,
priorityLevel,
);
} else {
newState = oldState;
}
if (
oldProps === newProps &&
oldState === newState &&
!hasContextChanged() &&
!(updateQueue !== null && updateQueue.hasForceUpdate)
) {
// If an update was already in progress, we should schedule an Update
// effect even though we're bailing out, so that cWU/cDU are called.
if (typeof instance.componentDidUpdate === 'function') {
if (
oldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.effectTag |= Update;
}
}
return false;
}
const shouldUpdate = checkShouldComponentUpdate(
workInProgress,
oldProps,
newProps,
oldState,
newState,
newContext,
);
if (shouldUpdate) {
if (typeof instance.componentWillUpdate === 'function') {
if (__DEV__) {
startPhaseTimer(workInProgress, 'componentWillUpdate');
}
instance.componentWillUpdate(newProps, newState, newContext);
if (__DEV__) {
stopPhaseTimer();
}
}
if (typeof instance.componentDidUpdate === 'function') {
workInProgress.effectTag |= Update;
}
} else {
// If an update was already in progress, we should schedule an Update
// effect even though we're bailing out, so that cWU/cDU are called.
if (typeof instance.componentDidUpdate === 'function') {
if (
oldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.effectTag |= Update;
}
}
// If shouldComponentUpdate returned false, we should still update the
// memoized props/state to indicate that this work can be reused.
memoizeProps(workInProgress, newProps);
memoizeState(workInProgress, newState);
}
// Update the existing instance's state, props, and context pointers even
// if shouldComponentUpdate returns false.
instance.props = newProps;
instance.state = newState;
instance.context = newContext;
return shouldUpdate;
}
return {
adoptClassInstance,
constructClassInstance,
mountClassInstance,
resumeMountClassInstance,
updateClassInstance,
};
};

Просмотреть файл

@ -1,541 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberCommitWork
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {HostConfig} from 'ReactFiberReconciler';
var ReactTypeOfWork = require('ReactTypeOfWork');
var {
ClassComponent,
HostRoot,
HostComponent,
HostText,
HostPortal,
CoroutineComponent,
} = ReactTypeOfWork;
var {commitCallbacks} = require('ReactFiberUpdateQueue');
var {onCommitUnmount} = require('ReactFiberDevToolsHook');
var {invokeGuardedCallback} = require('ReactErrorUtils');
var {
Placement,
Update,
Callback,
ContentReset,
} = require('ReactTypeOfSideEffect');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var {startPhaseTimer, stopPhaseTimer} = require('ReactDebugFiberPerf');
}
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
captureError: (failedFiber: Fiber, error: Error) => Fiber | null,
) {
const {
commitMount,
commitUpdate,
resetTextContent,
commitTextUpdate,
appendChild,
insertBefore,
removeChild,
getPublicInstance,
} = config;
if (__DEV__) {
var callComponentWillUnmountWithTimerInDev = function(current, instance) {
startPhaseTimer(current, 'componentWillUnmount');
instance.componentWillUnmount();
stopPhaseTimer();
};
}
// Capture errors so they don't interrupt unmounting.
function safelyCallComponentWillUnmount(current, instance) {
if (__DEV__) {
const unmountError = invokeGuardedCallback(
null,
callComponentWillUnmountWithTimerInDev,
null,
current,
instance,
);
if (unmountError) {
captureError(current, unmountError);
}
} else {
try {
instance.componentWillUnmount();
} catch (unmountError) {
captureError(current, unmountError);
}
}
}
function safelyDetachRef(current: Fiber) {
const ref = current.ref;
if (ref !== null) {
if (__DEV__) {
const refError = invokeGuardedCallback(null, ref, null, null);
if (refError !== null) {
captureError(current, refError);
}
} else {
try {
ref(null);
} catch (refError) {
captureError(current, refError);
}
}
}
}
function getHostParent(fiber: Fiber): I | C {
let parent = fiber.return;
while (parent !== null) {
switch (parent.tag) {
case HostComponent:
return parent.stateNode;
case HostRoot:
return parent.stateNode.containerInfo;
case HostPortal:
return parent.stateNode.containerInfo;
}
parent = parent.return;
}
invariant(
false,
'Expected to find a host parent. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
function getHostParentFiber(fiber: Fiber): Fiber {
let parent = fiber.return;
while (parent !== null) {
if (isHostParent(parent)) {
return parent;
}
parent = parent.return;
}
invariant(
false,
'Expected to find a host parent. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
function isHostParent(fiber: Fiber): boolean {
return (
fiber.tag === HostComponent ||
fiber.tag === HostRoot ||
fiber.tag === HostPortal
);
}
function getHostSibling(fiber: Fiber): ?I {
// We're going to search forward into the tree until we find a sibling host
// node. Unfortunately, if multiple insertions are done in a row we have to
// search past them. This leads to exponential search for the next sibling.
// TODO: Find a more efficient way to do this.
let node: Fiber = fiber;
siblings: while (true) {
// If we didn't find anything, let's try the next sibling.
while (node.sibling === null) {
if (node.return === null || isHostParent(node.return)) {
// If we pop out of the root or hit the parent the fiber we are the
// last sibling.
return null;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
while (node.tag !== HostComponent && node.tag !== HostText) {
// If it is not host node and, we might have a host node inside it.
// Try to search down until we find one.
if (node.effectTag & Placement) {
// If we don't have a child, try the siblings instead.
continue siblings;
}
// If we don't have a child, try the siblings instead.
// We also skip portals because they are not part of this host tree.
if (node.child === null || node.tag === HostPortal) {
continue siblings;
} else {
node.child.return = node;
node = node.child;
}
}
// Check if this host node is stable or about to be placed.
if (!(node.effectTag & Placement)) {
// Found it!
return node.stateNode;
}
}
}
function commitPlacement(finishedWork: Fiber): void {
// Recursively insert all host nodes into the parent.
const parentFiber = getHostParentFiber(finishedWork);
let parent;
switch (parentFiber.tag) {
case HostComponent:
parent = parentFiber.stateNode;
break;
case HostRoot:
parent = parentFiber.stateNode.containerInfo;
break;
case HostPortal:
parent = parentFiber.stateNode.containerInfo;
break;
default:
invariant(
false,
'Invalid host parent fiber. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
if (parentFiber.effectTag & ContentReset) {
// Reset the text content of the parent before doing any insertions
resetTextContent(parent);
// Clear ContentReset from the effect tag
parentFiber.effectTag &= ~ContentReset;
}
const before = getHostSibling(finishedWork);
// We only have the top Fiber that was inserted but we need recurse down its
// children to find all the terminal nodes.
let node: Fiber = finishedWork;
while (true) {
if (node.tag === HostComponent || node.tag === HostText) {
if (before) {
insertBefore(parent, node.stateNode, before);
} else {
appendChild(parent, node.stateNode);
}
} else if (node.tag === HostPortal) {
// If the insertion itself is a portal, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
if (node === finishedWork) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === finishedWork) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function commitNestedUnmounts(root: Fiber): void {
// While we're inside a removed host node we don't want to call
// removeChild on the inner nodes because they're removed by the top
// call anyway. We also want to call componentWillUnmount on all
// composites before this host node is removed from the tree. Therefore
// we do an inner loop while we're still inside the host node.
let node: Fiber = root;
while (true) {
commitUnmount(node);
// Visit children because they may contain more composite or host nodes.
// Skip portals because commitUnmount() currently visits them recursively.
if (node.child !== null && node.tag !== HostPortal) {
node.child.return = node;
node = node.child;
continue;
}
if (node === root) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === root) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function unmountHostComponents(parent, current): void {
// We only have the top Fiber that was inserted but we need recurse down its
// children to find all the terminal nodes.
let node: Fiber = current;
while (true) {
if (node.tag === HostComponent || node.tag === HostText) {
commitNestedUnmounts(node);
// After all the children have unmounted, it is now safe to remove the
// node from the tree.
removeChild(parent, node.stateNode);
// Don't visit children because we already visited them.
} else if (node.tag === HostPortal) {
// When we go into a portal, it becomes the parent to remove from.
// We will reassign it back when we pop the portal on the way up.
parent = node.stateNode.containerInfo;
// Visit children because portals might contain host components.
if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
} else {
commitUnmount(node);
// Visit children because we may find more host components below.
if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
}
if (node === current) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === current) {
return;
}
node = node.return;
if (node.tag === HostPortal) {
// When we go out of the portal, we need to restore the parent.
// Since we don't keep a stack of them, we will search for it.
parent = getHostParent(node);
}
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function commitDeletion(current: Fiber): void {
// Recursively delete all host nodes from the parent.
const parent = getHostParent(current);
// Detach refs and call componentWillUnmount() on the whole subtree.
unmountHostComponents(parent, current);
// Cut off the return pointers to disconnect it from the tree. Ideally, we
// should clear the child pointer of the parent alternate to let this
// get GC:ed but we don't know which for sure which parent is the current
// one so we'll settle for GC:ing the subtree of this child. This child
// itself will be GC:ed when the parent updates the next time.
current.return = null;
current.child = null;
if (current.alternate) {
current.alternate.child = null;
current.alternate.return = null;
}
}
// User-originating errors (lifecycles and refs) should not interrupt
// deletion, so don't let them throw. Host-originating errors should
// interrupt deletion, so it's okay
function commitUnmount(current: Fiber): void {
if (typeof onCommitUnmount === 'function') {
onCommitUnmount(current);
}
switch (current.tag) {
case ClassComponent: {
safelyDetachRef(current);
const instance = current.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
safelyCallComponentWillUnmount(current, instance);
}
return;
}
case HostComponent: {
safelyDetachRef(current);
return;
}
case CoroutineComponent: {
commitNestedUnmounts(current.stateNode);
return;
}
case HostPortal: {
// TODO: this is recursive.
// We are also not using this parent because
// the portal will get pushed immediately.
const parent = getHostParent(current);
unmountHostComponents(parent, current);
return;
}
}
}
function commitWork(current: Fiber | null, finishedWork: Fiber): void {
switch (finishedWork.tag) {
case ClassComponent: {
return;
}
case HostComponent: {
const instance: I = finishedWork.stateNode;
if (instance != null && current !== null) {
// Commit the work prepared earlier.
const newProps = finishedWork.memoizedProps;
const oldProps = current.memoizedProps;
const type = finishedWork.type;
// TODO: Type the updateQueue to be specific to host components.
const updatePayload: null | PL = (finishedWork.updateQueue: any);
finishedWork.updateQueue = null;
if (updatePayload !== null) {
commitUpdate(
instance,
updatePayload,
type,
oldProps,
newProps,
finishedWork,
);
}
}
return;
}
case HostText: {
invariant(
finishedWork.stateNode !== null && current !== null,
'This should only be done during updates. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
const textInstance: TI = finishedWork.stateNode;
const newText: string = finishedWork.memoizedProps;
const oldText: string = current.memoizedProps;
commitTextUpdate(textInstance, oldText, newText);
return;
}
case HostRoot: {
return;
}
case HostPortal: {
return;
}
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
function commitLifeCycles(current: Fiber | null, finishedWork: Fiber): void {
switch (finishedWork.tag) {
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) {
if (__DEV__) {
startPhaseTimer(finishedWork, 'componentDidMount');
}
instance.componentDidMount();
if (__DEV__) {
stopPhaseTimer();
}
} else {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
if (__DEV__) {
startPhaseTimer(finishedWork, 'componentDidUpdate');
}
instance.componentDidUpdate(prevProps, prevState);
if (__DEV__) {
stopPhaseTimer();
}
}
}
if (
finishedWork.effectTag & Callback &&
finishedWork.updateQueue !== null
) {
commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
}
return;
}
case HostRoot: {
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
const instance = finishedWork.child && finishedWork.child.stateNode;
commitCallbacks(finishedWork, updateQueue, instance);
}
return;
}
case HostComponent: {
const instance: I = finishedWork.stateNode;
// Renderers may schedule work to be done after host components are mounted
// (eg DOM renderer may schedule auto-focus for inputs and form controls).
// These effects should only be committed when components are first mounted,
// aka when there is no current/alternate.
if (current === null && finishedWork.effectTag & Update) {
const type = finishedWork.type;
const props = finishedWork.memoizedProps;
commitMount(instance, type, props, finishedWork);
}
return;
}
case HostText: {
// We have no life-cycles associated with text.
return;
}
case HostPortal: {
// We have no life-cycles associated with portals.
return;
}
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
const instance = getPublicInstance(finishedWork.stateNode);
ref(instance);
}
}
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
currentRef(null);
}
}
return {
commitPlacement,
commitDeletion,
commitWork,
commitLifeCycles,
commitAttachRef,
commitDetachRef,
};
};

Просмотреть файл

@ -1,361 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberCompleteWork
* @flow
*/
'use strict';
import type {ReactCoroutine} from 'ReactCoroutine';
import type {Fiber} from 'ReactFiber';
import type {HostContext} from 'ReactFiberHostContext';
import type {FiberRoot} from 'ReactFiberRoot';
import type {HostConfig} from 'ReactFiberReconciler';
var {reconcileChildFibers} = require('ReactChildFiber');
var {popContextProvider} = require('ReactFiberContext');
var ReactTypeOfWork = require('ReactTypeOfWork');
var ReactTypeOfSideEffect = require('ReactTypeOfSideEffect');
var {
IndeterminateComponent,
FunctionalComponent,
ClassComponent,
HostRoot,
HostComponent,
HostText,
HostPortal,
CoroutineComponent,
CoroutineHandlerPhase,
YieldComponent,
Fragment,
} = ReactTypeOfWork;
var {Ref, Update} = ReactTypeOfSideEffect;
if (__DEV__) {
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
}
var invariant = require('fbjs/lib/invariant');
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
hostContext: HostContext<C, CX>,
) {
const {
createInstance,
createTextInstance,
appendInitialChild,
finalizeInitialChildren,
prepareUpdate,
} = config;
const {
getRootHostContainer,
popHostContext,
getHostContext,
popHostContainer,
} = hostContext;
function markChildAsProgressed(current, workInProgress, priorityLevel) {
// We now have clones. Let's store them as the currently progressed work.
workInProgress.progressedChild = workInProgress.child;
workInProgress.progressedPriority = priorityLevel;
if (current !== null) {
// We also store it on the current. When the alternate swaps in we can
// continue from this point.
current.progressedChild = workInProgress.progressedChild;
current.progressedPriority = workInProgress.progressedPriority;
}
}
function markUpdate(workInProgress: Fiber) {
// Tag the fiber with an update effect. This turns a Placement into
// an UpdateAndPlacement.
workInProgress.effectTag |= Update;
}
function markRef(workInProgress: Fiber) {
workInProgress.effectTag |= Ref;
}
function appendAllYields(yields: Array<mixed>, workInProgress: Fiber) {
let node = workInProgress.stateNode;
if (node) {
node.return = workInProgress;
}
while (node !== null) {
if (
node.tag === HostComponent ||
node.tag === HostText ||
node.tag === HostPortal
) {
invariant(false, 'A coroutine cannot have host component children.');
} else if (node.tag === YieldComponent) {
yields.push(node.type);
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
while (node.sibling === null) {
if (node.return === null || node.return === workInProgress) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function moveCoroutineToHandlerPhase(
current: Fiber | null,
workInProgress: Fiber,
) {
var coroutine = (workInProgress.memoizedProps: ?ReactCoroutine);
invariant(
coroutine,
'Should be resolved by now. This error is likely caused by a bug in ' +
'React. Please file an issue.',
);
// First step of the coroutine has completed. Now we need to do the second.
// TODO: It would be nice to have a multi stage coroutine represented by a
// single component, or at least tail call optimize nested ones. Currently
// that requires additional fields that we don't want to add to the fiber.
// So this requires nested handlers.
// Note: This doesn't mutate the alternate node. I don't think it needs to
// since this stage is reset for every pass.
workInProgress.tag = CoroutineHandlerPhase;
// Build up the yields.
// TODO: Compare this to a generator or opaque helpers like Children.
var yields: Array<mixed> = [];
appendAllYields(yields, workInProgress);
var fn = coroutine.handler;
var props = coroutine.props;
var nextChildren = fn(props, yields);
var currentFirstChild = current !== null ? current.child : null;
// Inherit the priority of the returnFiber.
const priority = workInProgress.pendingWorkPriority;
workInProgress.child = reconcileChildFibers(
workInProgress,
currentFirstChild,
nextChildren,
priority,
);
markChildAsProgressed(current, workInProgress, priority);
return workInProgress.child;
}
function appendAllChildren(parent: I, workInProgress: Fiber) {
// We only have the top Fiber that was created but we need recurse down its
// children to find all the terminal nodes.
let node = workInProgress.child;
while (node !== null) {
if (node.tag === HostComponent || node.tag === HostText) {
appendInitialChild(parent, node.stateNode);
} else if (node.tag === HostPortal) {
// If we have a portal child, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else if (node.child !== null) {
node = node.child;
continue;
}
if (node === workInProgress) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === workInProgress) {
return;
}
node = node.return;
}
node = node.sibling;
}
}
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
): Fiber | null {
if (__DEV__) {
ReactDebugCurrentFiber.current = workInProgress;
}
switch (workInProgress.tag) {
case FunctionalComponent:
return null;
case ClassComponent: {
// We are leaving this subtree, so pop context if any.
popContextProvider(workInProgress);
return null;
}
case HostRoot: {
// TODO: Pop the host container after #8607 lands.
const fiberRoot = (workInProgress.stateNode: FiberRoot);
if (fiberRoot.pendingContext) {
fiberRoot.context = fiberRoot.pendingContext;
fiberRoot.pendingContext = null;
}
return null;
}
case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
const newProps = workInProgress.memoizedProps;
if (current !== null && workInProgress.stateNode != null) {
// If we have an alternate, that means this is an update and we need to
// schedule a side-effect to do the updates.
const oldProps = current.memoizedProps;
// If we get updated because one of our children updated, we don't
// have newProps so we'll have to reuse them.
// TODO: Split the update API as separate for the props vs. children.
// Even better would be if children weren't special cased at all tho.
const instance: I = workInProgress.stateNode;
const currentHostContext = getHostContext();
const updatePayload = prepareUpdate(
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
// TODO: Type this specific to this type of component.
workInProgress.updateQueue = (updatePayload: any);
// If the update payload indicates that there is a change or if there
// is a new ref we mark this as an update.
if (updatePayload) {
markUpdate(workInProgress);
}
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
if (!newProps) {
invariant(
workInProgress.stateNode !== null,
'We must have new props for new mounts. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
// This can happen when we abort work.
return null;
}
const currentHostContext = getHostContext();
// TODO: Move createInstance to beginWork and keep it on a context
// "stack" as the parent. Then append children as we go in beginWork
// or completeWork depending on we want to add then top->down or
// bottom->up. Top->down is faster in IE11.
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
appendAllChildren(instance, workInProgress);
// Certain renderers require commit-time effects for initial mount.
// (eg DOM renderer supports auto-focus for certain elements).
// Make sure such renderers get scheduled for later work.
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
)
) {
markUpdate(workInProgress);
}
workInProgress.stateNode = instance;
if (workInProgress.ref !== null) {
// If there is a ref on a host node we need to schedule a callback
markRef(workInProgress);
}
}
return null;
}
case HostText: {
let newText = workInProgress.memoizedProps;
if (current && workInProgress.stateNode != null) {
const oldText = current.memoizedProps;
// If we have an alternate, that means this is an update and we need
// to schedule a side-effect to do the updates.
if (oldText !== newText) {
markUpdate(workInProgress);
}
} else {
if (typeof newText !== 'string') {
invariant(
workInProgress.stateNode !== null,
'We must have new props for new mounts. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
// This can happen when we abort work.
return null;
}
const rootContainerInstance = getRootHostContainer();
const currentHostContext = getHostContext();
const textInstance = createTextInstance(
newText,
rootContainerInstance,
currentHostContext,
workInProgress,
);
workInProgress.stateNode = textInstance;
}
return null;
}
case CoroutineComponent:
return moveCoroutineToHandlerPhase(current, workInProgress);
case CoroutineHandlerPhase:
// Reset the tag to now be a first phase coroutine.
workInProgress.tag = CoroutineComponent;
return null;
case YieldComponent:
// Does nothing.
return null;
case Fragment:
return null;
case HostPortal:
// TODO: Only mark this as an update if we have any pending callbacks.
markUpdate(workInProgress);
popHostContainer(workInProgress);
return null;
// Error cases
case IndeterminateComponent:
invariant(
false,
'An indeterminate component should have become determinate before ' +
'completing. This error is likely caused by a bug in React. Please ' +
'file an issue.',
);
// eslint-disable-next-line no-fallthrough
default:
invariant(
false,
'Unknown unit of work tag. This error is likely caused by a bug in ' +
'React. Please file an issue.',
);
}
}
return {
completeWork,
};
};

Просмотреть файл

@ -1,289 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberContext
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {StackCursor} from 'ReactFiberStack';
var checkPropTypes = require('prop-types/checkPropTypes');
var emptyObject = require('fbjs/lib/emptyObject');
var getComponentName = require('getComponentName');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var {isFiberMounted} = require('ReactFiberTreeReflection');
var {ClassComponent, HostRoot} = require('ReactTypeOfWork');
const {createCursor, pop, push} = require('ReactFiberStack');
if (__DEV__) {
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
var {startPhaseTimer, stopPhaseTimer} = require('ReactDebugFiberPerf');
var warnedAboutMissingGetChildContext = {};
}
// A cursor to the current merged context object on the stack.
let contextStackCursor: StackCursor<Object> = createCursor(emptyObject);
// A cursor to a boolean indicating whether the context has changed.
let didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false);
// Keep track of the previous context object that was on the stack.
// We use this to get access to the parent context after we have already
// pushed the next context provider, and now need to merge their contexts.
let previousContext: Object = emptyObject;
function getUnmaskedContext(workInProgress: Fiber): Object {
const hasOwnContext = isContextProvider(workInProgress);
if (hasOwnContext) {
// If the fiber is a context provider itself, when we read its context
// we have already pushed its own child context on the stack. A context
// provider should not "see" its own child context. Therefore we read the
// previous (parent) context instead for a context provider.
return previousContext;
}
return contextStackCursor.current;
}
exports.getUnmaskedContext = getUnmaskedContext;
function cacheContext(
workInProgress: Fiber,
unmaskedContext: Object,
maskedContext: Object,
) {
const instance = workInProgress.stateNode;
instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
}
exports.cacheContext = cacheContext;
exports.getMaskedContext = function(
workInProgress: Fiber,
unmaskedContext: Object,
) {
const type = workInProgress.type;
const contextTypes = type.contextTypes;
if (!contextTypes) {
return emptyObject;
}
// Avoid recreating masked context unless unmasked context has changed.
// Failing to do this will result in unnecessary calls to componentWillReceiveProps.
// This may trigger infinite loops if componentWillReceiveProps calls setState.
const instance = workInProgress.stateNode;
if (
instance &&
instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
) {
return instance.__reactInternalMemoizedMaskedChildContext;
}
const context = {};
for (let key in contextTypes) {
context[key] = unmaskedContext[key];
}
if (__DEV__) {
const name = getComponentName(workInProgress) || 'Unknown';
ReactDebugCurrentFrame.current = workInProgress;
checkPropTypes(
contextTypes,
context,
'context',
name,
ReactDebugCurrentFrame.getStackAddendum,
);
ReactDebugCurrentFrame.current = null;
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// Context is created before the class component is instantiated so check for instance.
if (instance) {
cacheContext(workInProgress, unmaskedContext, context);
}
return context;
};
exports.hasContextChanged = function(): boolean {
return didPerformWorkStackCursor.current;
};
function isContextConsumer(fiber: Fiber): boolean {
return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
}
exports.isContextConsumer = isContextConsumer;
function isContextProvider(fiber: Fiber): boolean {
return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
}
exports.isContextProvider = isContextProvider;
function popContextProvider(fiber: Fiber): void {
if (!isContextProvider(fiber)) {
return;
}
pop(didPerformWorkStackCursor, fiber);
pop(contextStackCursor, fiber);
}
exports.popContextProvider = popContextProvider;
exports.pushTopLevelContextObject = function(
fiber: Fiber,
context: Object,
didChange: boolean,
): void {
invariant(
contextStackCursor.cursor == null,
'Unexpected context found on stack',
);
push(contextStackCursor, context, fiber);
push(didPerformWorkStackCursor, didChange, fiber);
};
function processChildContext(
fiber: Fiber,
parentContext: Object,
isReconciling: boolean,
): Object {
const instance = fiber.stateNode;
const childContextTypes = fiber.type.childContextTypes;
// TODO (bvaughn) Replace this behavior with an invariant() in the future.
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
if (typeof instance.getChildContext !== 'function') {
if (__DEV__) {
const componentName = getComponentName(fiber) || 'Unknown';
if (!warnedAboutMissingGetChildContext[componentName]) {
warnedAboutMissingGetChildContext[componentName] = true;
warning(
false,
'%s.childContextTypes is specified but there is no getChildContext() method ' +
'on the instance. You can either define getChildContext() on %s or remove ' +
'childContextTypes from it.',
componentName,
componentName,
);
}
}
return parentContext;
}
let childContext;
if (__DEV__) {
ReactDebugCurrentFiber.phase = 'getChildContext';
startPhaseTimer(fiber, 'getChildContext');
childContext = instance.getChildContext();
stopPhaseTimer();
ReactDebugCurrentFiber.phase = null;
} else {
childContext = instance.getChildContext();
}
for (let contextKey in childContext) {
invariant(
contextKey in childContextTypes,
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
getComponentName(fiber) || 'Unknown',
contextKey,
);
}
if (__DEV__) {
const name = getComponentName(fiber) || 'Unknown';
// We can only provide accurate element stacks if we pass work-in-progress tree
// during the begin or complete phase. However currently this function is also
// called from unstable_renderSubtree legacy implementation. In this case it unsafe to
// assume anything about the given fiber. We won't pass it down if we aren't sure.
// TODO: remove this hack when we delete unstable_renderSubtree in Fiber.
const workInProgress = isReconciling ? fiber : null;
ReactDebugCurrentFrame.current = workInProgress;
checkPropTypes(
childContextTypes,
childContext,
'child context',
name,
ReactDebugCurrentFrame.getStackAddendum,
);
ReactDebugCurrentFrame.current = null;
}
return {...parentContext, ...childContext};
}
exports.processChildContext = processChildContext;
exports.pushContextProvider = function(workInProgress: Fiber): boolean {
if (!isContextProvider(workInProgress)) {
return false;
}
const instance = workInProgress.stateNode;
// We push the context as early as possible to ensure stack integrity.
// If the instance does not exist yet, we will push null at first,
// and replace it on the stack later when invalidating the context.
const memoizedMergedChildContext =
(instance && instance.__reactInternalMemoizedMergedChildContext) ||
emptyObject;
// Remember the parent context so we can merge with it later.
previousContext = contextStackCursor.current;
push(contextStackCursor, memoizedMergedChildContext, workInProgress);
push(didPerformWorkStackCursor, false, workInProgress);
return true;
};
exports.invalidateContextProvider = function(workInProgress: Fiber): void {
const instance = workInProgress.stateNode;
invariant(instance, 'Expected to have an instance by this point.');
// Merge parent and own context.
const mergedContext = processChildContext(
workInProgress,
previousContext,
true,
);
instance.__reactInternalMemoizedMergedChildContext = mergedContext;
// Replace the old (or empty) context with the new one.
// It is important to unwind the context in the reverse order.
pop(didPerformWorkStackCursor, workInProgress);
pop(contextStackCursor, workInProgress);
// Now push the new context and mark that it has changed.
push(contextStackCursor, mergedContext, workInProgress);
push(didPerformWorkStackCursor, true, workInProgress);
};
exports.resetContext = function(): void {
previousContext = emptyObject;
contextStackCursor.current = emptyObject;
didPerformWorkStackCursor.current = false;
};
exports.findCurrentUnmaskedContext = function(fiber: Fiber): Object {
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
// makes sense elsewhere
invariant(
isFiberMounted(fiber) && fiber.tag === ClassComponent,
'Expected subtree parent to be a mounted class component',
);
let node: Fiber = fiber;
while (node.tag !== HostRoot) {
if (isContextProvider(node)) {
return node.stateNode.__reactInternalMemoizedMergedChildContext;
}
const parent = node.return;
invariant(parent, 'Found unexpected detached subtree parent');
node = parent;
}
return node.stateNode.context;
};

Просмотреть файл

@ -1,72 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberDevToolsHook
* @flow
*/
'use strict';
var warning = require('fbjs/lib/warning');
import type {Fiber} from 'ReactFiber';
import type {FiberRoot} from 'ReactFiberRoot';
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
let rendererID = null;
let injectInternals = null;
let onCommitRoot = null;
let onCommitUnmount = null;
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
__REACT_DEVTOOLS_GLOBAL_HOOK__.supportsFiber
) {
let {
inject,
onCommitFiberRoot,
onCommitFiberUnmount,
} = __REACT_DEVTOOLS_GLOBAL_HOOK__;
injectInternals = function(internals: Object) {
warning(rendererID == null, 'Cannot inject into DevTools twice.');
rendererID = inject(internals);
};
onCommitRoot = function(root: FiberRoot) {
if (rendererID == null) {
return;
}
try {
onCommitFiberRoot(rendererID, root);
} catch (err) {
// Catch all errors because it is unsafe to throw in the commit phase.
if (__DEV__) {
warning(false, 'React DevTools encountered an error: %s', err);
}
}
};
onCommitUnmount = function(fiber: Fiber) {
if (rendererID == null) {
return;
}
try {
onCommitFiberUnmount(rendererID, fiber);
} catch (err) {
// Catch all errors because it is unsafe to throw in the commit phase.
if (__DEV__) {
warning(false, 'React DevTools encountered an error: %s', err);
}
}
};
}
exports.injectInternals = injectInternals;
exports.onCommitRoot = onCommitRoot;
exports.onCommitUnmount = onCommitUnmount;

Просмотреть файл

@ -1,116 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberErrorLogger
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
import type {CapturedError} from 'ReactFiberScheduler';
const defaultShowDialog = (capturedError: CapturedError) => true;
let showDialog = defaultShowDialog;
function logCapturedError(capturedError: CapturedError): void {
const logError = showDialog(capturedError);
// Allow injected showDialog() to prevent default console.error logging.
// This enables renderers like ReactNative to better manage redbox behavior.
if (logError === false) {
return;
}
if (__DEV__) {
const {
componentName,
componentStack,
error,
errorBoundaryName,
errorBoundaryFound,
willRetry,
} = capturedError;
const {message, name, stack} = error;
const errorSummary = message ? `${name}: ${message}` : name;
const componentNameMessage = componentName
? `React caught an error thrown by ${componentName}.`
: 'React caught an error thrown by one of your components.';
// Error stack varies by browser, eg:
// Chrome prepends the Error name and type.
// Firefox, Safari, and IE don't indent the stack lines.
// Format it in a consistent way for error logging.
let formattedCallStack = stack.slice(0, errorSummary.length) ===
errorSummary
? stack.slice(errorSummary.length)
: stack;
formattedCallStack = formattedCallStack
.trim()
.split('\n')
.map(line => `\n ${line.trim()}`)
.join();
let errorBoundaryMessage;
// errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow.
if (errorBoundaryFound && errorBoundaryName) {
if (willRetry) {
errorBoundaryMessage =
`React will try to recreate this component tree from scratch ` +
`using the error boundary you provided, ${errorBoundaryName}.`;
} else {
errorBoundaryMessage =
`This error was initially handled by the error boundary ${errorBoundaryName}. ` +
`Recreating the tree from scratch failed so React will unmount the tree.`;
}
} else {
// TODO Link to unstable_handleError() documentation once it exists.
errorBoundaryMessage =
'Consider adding an error boundary to your tree to customize error handling behavior.';
}
console.error(
`${componentNameMessage} You should fix this error in your code. ${errorBoundaryMessage}\n\n` +
`${errorSummary}\n\n` +
`The error is located at: ${componentStack}\n\n` +
`The error was thrown at: ${formattedCallStack}`,
);
}
if (!__DEV__) {
const {error} = capturedError;
console.error(
`React caught an error thrown by one of your components.\n\n${error.stack}`,
);
}
}
exports.injection = {
/**
* Display custom dialog for lifecycle errors.
* Return false to prevent default behavior of logging to console.error.
*/
injectDialog(fn: (e: CapturedError) => boolean) {
invariant(
showDialog === defaultShowDialog,
'The custom dialog was already injected.',
);
invariant(
typeof fn === 'function',
'Injected showDialog() must be a function.',
);
showDialog = fn;
},
};
exports.logCapturedError = logCapturedError;

Просмотреть файл

@ -1,130 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberHostContext
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {HostConfig} from 'ReactFiberReconciler';
import type {StackCursor} from 'ReactFiberStack';
const {createCursor, pop, push} = require('ReactFiberStack');
const invariant = require('fbjs/lib/invariant');
declare class NoContextT {}
const NO_CONTEXT: NoContextT = ({}: any);
export type HostContext<C, CX> = {
getHostContext(): CX,
getRootHostContainer(): C,
popHostContainer(fiber: Fiber): void,
popHostContext(fiber: Fiber): void,
pushHostContainer(fiber: Fiber, container: C): void,
pushHostContext(fiber: Fiber): void,
resetHostContainer(): void,
};
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
): HostContext<C, CX> {
const {getChildHostContext, getRootHostContext} = config;
let contextStackCursor: StackCursor<CX | NoContextT> = createCursor(
NO_CONTEXT,
);
let contextFiberStackCursor: StackCursor<Fiber | NoContextT> = createCursor(
NO_CONTEXT,
);
let rootInstanceStackCursor: StackCursor<C | NoContextT> = createCursor(
NO_CONTEXT,
);
function requiredContext<Value>(c: Value | NoContextT): Value {
invariant(
c !== NO_CONTEXT,
'Expected host context to exist. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
return (c: any);
}
function getRootHostContainer(): C {
const rootInstance = requiredContext(rootInstanceStackCursor.current);
return rootInstance;
}
function pushHostContainer(fiber: Fiber, nextRootInstance: C) {
// Push current root instance onto the stack;
// This allows us to reset root when portals are popped.
push(rootInstanceStackCursor, nextRootInstance, fiber);
const nextRootContext = getRootHostContext(nextRootInstance);
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextRootContext, fiber);
}
function popHostContainer(fiber: Fiber) {
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
pop(rootInstanceStackCursor, fiber);
}
function getHostContext(): CX {
const context = requiredContext(contextStackCursor.current);
return context;
}
function pushHostContext(fiber: Fiber): void {
const rootInstance = requiredContext(rootInstanceStackCursor.current);
const context = requiredContext(contextStackCursor.current);
const nextContext = getChildHostContext(context, fiber.type, rootInstance);
// Don't push this Fiber's context unless it's unique.
if (context === nextContext) {
return;
}
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
}
function popHostContext(fiber: Fiber): void {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
if (contextFiberStackCursor.current !== fiber) {
return;
}
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
function resetHostContainer() {
contextStackCursor.current = NO_CONTEXT;
rootInstanceStackCursor.current = NO_CONTEXT;
}
return {
getHostContext,
getRootHostContainer,
popHostContainer,
popHostContext,
pushHostContainer,
pushHostContext,
resetHostContainer,
};
};

Просмотреть файл

@ -1,23 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberInstrumentation
* @flow
*/
'use strict';
// This lets us hook into Fiber to debug what it's doing.
// See https://github.com/facebook/react/pull/8033.
// This is not part of the public API, not even for React DevTools.
// You may only inject a debugTool if you work on React Fiber itself.
var ReactFiberInstrumentation = {
debugTool: null,
};
module.exports = ReactFiberInstrumentation;

Просмотреть файл

@ -1,271 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberReconciler
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {FiberRoot} from 'ReactFiberRoot';
import type {PriorityLevel} from 'ReactPriorityLevel';
import type {ReactNodeList} from 'ReactTypes';
var ReactFeatureFlags = require('ReactFeatureFlags');
var {addTopLevelUpdate} = require('ReactFiberUpdateQueue');
var {
findCurrentUnmaskedContext,
isContextProvider,
processChildContext,
} = require('ReactFiberContext');
var {createFiberRoot} = require('ReactFiberRoot');
var ReactFiberScheduler = require('ReactFiberScheduler');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
var getComponentName = require('getComponentName');
}
var {findCurrentHostFiber} = require('ReactFiberTreeReflection');
var getContextForSubtree = require('getContextForSubtree');
export type Deadline = {
timeRemaining: () => number,
};
type OpaqueHandle = Fiber;
type OpaqueRoot = FiberRoot;
export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
getRootHostContext(rootContainerInstance: C): CX,
getChildHostContext(parentHostContext: CX, type: T, instance: C): CX,
getPublicInstance(instance: I | TI): PI,
createInstance(
type: T,
props: P,
rootContainerInstance: C,
hostContext: CX,
internalInstanceHandle: OpaqueHandle,
): I,
appendInitialChild(parentInstance: I, child: I | TI): void,
finalizeInitialChildren(
parentInstance: I,
type: T,
props: P,
rootContainerInstance: C,
): boolean,
prepareUpdate(
instance: I,
type: T,
oldProps: P,
newProps: P,
rootContainerInstance: C,
hostContext: CX,
): null | PL,
commitUpdate(
instance: I,
updatePayload: PL,
type: T,
oldProps: P,
newProps: P,
internalInstanceHandle: OpaqueHandle,
): void,
commitMount(
instance: I,
type: T,
newProps: P,
internalInstanceHandle: OpaqueHandle,
): void,
shouldSetTextContent(props: P): boolean,
resetTextContent(instance: I): void,
shouldDeprioritizeSubtree(type: T, props: P): boolean,
createTextInstance(
text: string,
rootContainerInstance: C,
hostContext: CX,
internalInstanceHandle: OpaqueHandle,
): TI,
commitTextUpdate(textInstance: TI, oldText: string, newText: string): void,
appendChild(parentInstance: I | C, child: I | TI): void,
insertBefore(parentInstance: I | C, child: I | TI, beforeChild: I | TI): void,
removeChild(parentInstance: I | C, child: I | TI): void,
scheduleAnimationCallback(callback: () => void): number | void,
scheduleDeferredCallback(
callback: (deadline: Deadline) => void,
): number | void,
prepareForCommit(): void,
resetAfterCommit(): void,
useSyncScheduling?: boolean,
};
export type Reconciler<C, I, TI> = {
createContainer(containerInfo: C): OpaqueRoot,
updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?ReactComponent<any, any, any>,
callback: ?Function,
): void,
performWithPriority(priorityLevel: PriorityLevel, fn: Function): void,
batchedUpdates<A>(fn: () => A): A,
unbatchedUpdates<A>(fn: () => A): A,
syncUpdates<A>(fn: () => A): A,
deferredUpdates<A>(fn: () => A): A,
// Used to extract the return value from the initial render. Legacy API.
getPublicRootInstance(
container: OpaqueRoot,
): ReactComponent<any, any, any> | TI | I | null,
// Use for findDOMNode/findHostNode. Legacy API.
findHostInstance(component: Fiber): I | TI | null,
};
getContextForSubtree._injectFiber(function(fiber: Fiber) {
const parentContext = findCurrentUnmaskedContext(fiber);
return isContextProvider(fiber)
? processChildContext(fiber, parentContext, false)
: parentContext;
});
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
): Reconciler<C, I, TI> {
var {
scheduleUpdate,
getPriorityContext,
performWithPriority,
batchedUpdates,
unbatchedUpdates,
syncUpdates,
deferredUpdates,
} = ReactFiberScheduler(config);
function scheduleTopLevelUpdate(
current: Fiber,
element: ReactNodeList,
callback: ?Function,
) {
if (__DEV__) {
if (
ReactDebugCurrentFiber.phase === 'render' &&
ReactDebugCurrentFiber.current !== null
) {
warning(
false,
'Render methods should be a pure function of props and state; ' +
'triggering nested component updates from render is not allowed. ' +
'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
'Check the render method of %s.',
getComponentName(ReactDebugCurrentFiber.current) || 'Unknown',
);
}
}
// Check if the top-level element is an async wrapper component. If so, treat
// updates to the root as async. This is a bit weird but lets us avoid a separate
// `renderAsync` API.
const forceAsync =
(ReactFeatureFlags : any).enableAsyncSubtreeAPI &&
element != null &&
element.type != null &&
(element.type: any).unstable_asyncUpdates === true;
const priorityLevel = getPriorityContext(current, forceAsync);
const nextState = {element};
callback = callback === undefined ? null : callback;
if (__DEV__) {
warning(
callback === null || typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
addTopLevelUpdate(current, nextState, callback, priorityLevel);
scheduleUpdate(current, priorityLevel);
}
return {
createContainer(containerInfo: C): OpaqueRoot {
return createFiberRoot(containerInfo);
},
updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?ReactComponent<any, any, any>,
callback: ?Function,
): void {
// TODO: If this is a nested container, this won't be the root.
const current = container.current;
if (__DEV__) {
if (ReactFiberInstrumentation.debugTool) {
if (current.alternate === null) {
ReactFiberInstrumentation.debugTool.onMountContainer(container);
} else if (element === null) {
ReactFiberInstrumentation.debugTool.onUnmountContainer(container);
} else {
ReactFiberInstrumentation.debugTool.onUpdateContainer(container);
}
}
}
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
scheduleTopLevelUpdate(current, element, callback);
},
performWithPriority,
batchedUpdates,
unbatchedUpdates,
syncUpdates,
deferredUpdates,
getPublicRootInstance(
container: OpaqueRoot,
): ReactComponent<any, any, any> | I | TI | null {
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
return containerFiber.child.stateNode;
},
findHostInstance(fiber: Fiber): I | TI | null {
const hostFiber = findCurrentHostFiber(fiber);
if (hostFiber === null) {
return null;
}
return hostFiber.stateNode;
},
};
};

Просмотреть файл

@ -1,47 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberRoot
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
const {createHostRootFiber} = require('ReactFiber');
export type FiberRoot = {
// Any additional information from the host associated with this root.
containerInfo: any,
// The currently active root fiber. This is the mutable root of the tree.
current: Fiber,
// Determines if this root has already been added to the schedule for work.
isScheduled: boolean,
// The work schedule is a linked list.
nextScheduledRoot: FiberRoot | null,
// Top context object, used by renderSubtreeIntoContainer
context: Object | null,
pendingContext: Object | null,
};
exports.createFiberRoot = function(containerInfo: any): FiberRoot {
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
const uninitializedFiber = createHostRootFiber();
const root = {
current: uninitializedFiber,
containerInfo: containerInfo,
isScheduled: false,
nextScheduledRoot: null,
context: null,
pendingContext: null,
};
uninitializedFiber.stateNode = root;
return root;
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,92 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberStack
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
export type StackCursor<T> = {
current: T,
};
const warning = require('fbjs/lib/warning');
const valueStack: Array<any> = [];
if (__DEV__) {
var fiberStack: Array<Fiber | null> = [];
}
let index = -1;
exports.createCursor = function<T>(defaultValue: T): StackCursor<T> {
return {
current: defaultValue,
};
};
exports.isEmpty = function(): boolean {
return index === -1;
};
exports.pop = function<T>(cursor: StackCursor<T>, fiber: Fiber): void {
if (index < 0) {
if (__DEV__) {
warning(false, 'Unexpected pop.');
}
return;
}
if (__DEV__) {
if (fiber !== fiberStack[index]) {
warning(false, 'Unexpected Fiber popped.');
}
}
cursor.current = valueStack[index];
valueStack[index] = null;
if (__DEV__) {
fiberStack[index] = null;
}
index--;
};
exports.push = function<T>(
cursor: StackCursor<T>,
value: T,
fiber: Fiber,
): void {
index++;
valueStack[index] = cursor.current;
if (__DEV__) {
fiberStack[index] = fiber;
}
cursor.current = value;
};
exports.reset = function(): void {
while (index > -1) {
valueStack[index] = null;
if (__DEV__) {
fiberStack[index] = null;
}
index--;
}
};

Просмотреть файл

@ -1,265 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberTreeReflection
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
var ReactInstanceMap = require('ReactInstanceMap');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var getComponentName = require('getComponentName');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
var {
HostRoot,
HostComponent,
HostText,
ClassComponent,
} = require('ReactTypeOfWork');
var {NoEffect, Placement} = require('ReactTypeOfSideEffect');
var MOUNTING = 1;
var MOUNTED = 2;
var UNMOUNTED = 3;
function isFiberMountedImpl(fiber: Fiber): number {
let node = fiber;
if (!fiber.alternate) {
// If there is no alternate, this might be a new tree that isn't inserted
// yet. If it is, then it will have a pending insertion effect on it.
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
while (node.return) {
node = node.return;
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
}
} else {
while (node.return) {
node = node.return;
}
}
if (node.tag === HostRoot) {
// TODO: Check if this was a nested HostRoot when used with
// renderContainerIntoSubtree.
return MOUNTED;
}
// If we didn't hit the root, that means that we're in an disconnected tree
// that has been unmounted.
return UNMOUNTED;
}
exports.isFiberMounted = function(fiber: Fiber): boolean {
return isFiberMountedImpl(fiber) === MOUNTED;
};
exports.isMounted = function(
component: ReactComponent<any, any, any>,
): boolean {
if (__DEV__) {
const owner = (ReactCurrentOwner.current: any);
if (owner !== null && owner.tag === ClassComponent) {
const ownerFiber: Fiber = owner;
const instance = ownerFiber.stateNode;
warning(
instance._warnedAboutRefsInRender,
'%s is accessing isMounted inside its render() function. ' +
'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(ownerFiber) || 'A component',
);
instance._warnedAboutRefsInRender = true;
}
}
var fiber: ?Fiber = ReactInstanceMap.get(component);
if (!fiber) {
return false;
}
return isFiberMountedImpl(fiber) === MOUNTED;
};
function assertIsMounted(fiber) {
invariant(
isFiberMountedImpl(fiber) === MOUNTED,
'Unable to find node on an unmounted component.',
);
}
function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
let alternate = fiber.alternate;
if (!alternate) {
// If there is no alternate, then we only need to check if it is mounted.
const state = isFiberMountedImpl(fiber);
invariant(
state !== UNMOUNTED,
'Unable to find node on an unmounted component.',
);
if (state === MOUNTING) {
return null;
}
return fiber;
}
// If we have two possible branches, we'll walk backwards up to the root
// to see what path the root points to. On the way we may hit one of the
// special cases and we'll deal with them.
let a = fiber;
let b = alternate;
while (true) {
let parentA = a.return;
let parentB = parentA ? parentA.alternate : null;
if (!parentA || !parentB) {
// We're at the root.
break;
}
// If both copies of the parent fiber point to the same child, we can
// assume that the child is current. This happens when we bailout on low
// priority: the bailed out fiber's child reuses the current child.
if (parentA.child === parentB.child) {
let child = parentA.child;
while (child) {
if (child === a) {
// We've determined that A is the current branch.
assertIsMounted(parentA);
return fiber;
}
if (child === b) {
// We've determined that B is the current branch.
assertIsMounted(parentA);
return alternate;
}
child = child.sibling;
}
// We should never have an alternate for any mounting node. So the only
// way this could possibly happen is if this was unmounted, if at all.
invariant(false, 'Unable to find node on an unmounted component.');
}
if (a.return !== b.return) {
// The return pointer of A and the return pointer of B point to different
// fibers. We assume that return pointers never criss-cross, so A must
// belong to the child set of A.return, and B must belong to the child
// set of B.return.
a = parentA;
b = parentB;
} else {
// The return pointers pointer to the same fiber. We'll have to use the
// default, slow path: scan the child sets of each parent alternate to see
// which child belongs to which set.
//
// Search parent A's child set
let didFindChild = false;
let child = parentA.child;
while (child) {
if (child === a) {
didFindChild = true;
a = parentA;
b = parentB;
break;
}
if (child === b) {
didFindChild = true;
b = parentA;
a = parentB;
break;
}
child = child.sibling;
}
if (!didFindChild) {
// Search parent B's child set
child = parentB.child;
while (child) {
if (child === a) {
didFindChild = true;
a = parentB;
b = parentA;
break;
}
if (child === b) {
didFindChild = true;
b = parentB;
a = parentA;
break;
}
child = child.sibling;
}
invariant(
didFindChild,
'Child was not found in either parent set. This indicates a bug ' +
'related to the return pointer.',
);
}
}
invariant(
a.alternate === b,
"Return fibers should always be each others' alternates.",
);
}
// If the root is not a host container, we're in a disconnected tree. I.e.
// unmounted.
invariant(
a.tag === HostRoot,
'Unable to find node on an unmounted component.',
);
if (a.stateNode.current === a) {
// We've determined that A is the current branch.
return fiber;
}
// Otherwise B has to be current branch.
return alternate;
}
exports.findCurrentFiberUsingSlowPath = findCurrentFiberUsingSlowPath;
exports.findCurrentHostFiber = function(parent: Fiber): Fiber | null {
const currentParent = findCurrentFiberUsingSlowPath(parent);
if (!currentParent) {
return null;
}
// Next we'll drill down this component to find the first HostComponent/Text.
let node: Fiber = currentParent;
while (true) {
if (node.tag === HostComponent || node.tag === HostText) {
return node;
} else if (node.child) {
// TODO: If we hit a Portal, we're supposed to skip it.
node.child.return = node;
node = node.child;
continue;
}
if (node === currentParent) {
return null;
}
while (!node.sibling) {
if (!node.return || node.return === currentParent) {
return null;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
// Flow needs the return null here, but ESLint complains about it.
// eslint-disable-next-line no-unreachable
return null;
};

Просмотреть файл

@ -1,509 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberUpdateQueue
* @flow
*/
'use strict';
import type {Fiber} from 'ReactFiber';
import type {PriorityLevel} from 'ReactPriorityLevel';
const {Callback: CallbackEffect} = require('ReactTypeOfSideEffect');
const {
NoWork,
SynchronousPriority,
TaskPriority,
} = require('ReactPriorityLevel');
const invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
type PartialState<State, Props> =
| $Subtype<State>
| ((prevState: State, props: Props) => $Subtype<State>);
// Callbacks are not validated until invocation
type Callback = mixed;
type Update = {
priorityLevel: PriorityLevel,
partialState: PartialState<any, any>,
callback: Callback | null,
isReplace: boolean,
isForced: boolean,
isTopLevelUnmount: boolean,
next: Update | null,
};
// Singly linked-list of updates. When an update is scheduled, it is added to
// the queue of the current fiber and the work-in-progress fiber. The two queues
// are separate but they share a persistent structure.
//
// During reconciliation, updates are removed from the work-in-progress fiber,
// but they remain on the current fiber. That ensures that if a work-in-progress
// is aborted, the aborted updates are recovered by cloning from current.
//
// The work-in-progress queue is always a subset of the current queue.
//
// When the tree is committed, the work-in-progress becomes the current.
export type UpdateQueue = {
first: Update | null,
last: Update | null,
hasForceUpdate: boolean,
callbackList: null | Array<Callback>,
// Dev only
isProcessing?: boolean,
};
function comparePriority(a: PriorityLevel, b: PriorityLevel): number {
// When comparing update priorities, treat sync and Task work as equal.
// TODO: Could we avoid the need for this by always coercing sync priority
// to Task when scheduling an update?
if (
(a === TaskPriority || a === SynchronousPriority) &&
(b === TaskPriority || b === SynchronousPriority)
) {
return 0;
}
if (a === NoWork && b !== NoWork) {
return -255;
}
if (a !== NoWork && b === NoWork) {
return 255;
}
return a - b;
}
// Ensures that a fiber has an update queue, creating a new one if needed.
// Returns the new or existing queue.
function ensureUpdateQueue(fiber: Fiber): UpdateQueue {
if (fiber.updateQueue !== null) {
// We already have an update queue.
return fiber.updateQueue;
}
let queue;
if (__DEV__) {
queue = {
first: null,
last: null,
hasForceUpdate: false,
callbackList: null,
isProcessing: false,
};
} else {
queue = {
first: null,
last: null,
hasForceUpdate: false,
callbackList: null,
};
}
fiber.updateQueue = queue;
return queue;
}
// Clones an update queue from a source fiber onto its alternate.
function cloneUpdateQueue(
current: Fiber,
workInProgress: Fiber,
): UpdateQueue | null {
const currentQueue = current.updateQueue;
if (currentQueue === null) {
// The source fiber does not have an update queue.
workInProgress.updateQueue = null;
return null;
}
// If the alternate already has a queue, reuse the previous object.
const altQueue = workInProgress.updateQueue !== null
? workInProgress.updateQueue
: {};
altQueue.first = currentQueue.first;
altQueue.last = currentQueue.last;
// These fields are invalid by the time we clone from current. Reset them.
altQueue.hasForceUpdate = false;
altQueue.callbackList = null;
altQueue.isProcessing = false;
workInProgress.updateQueue = altQueue;
return altQueue;
}
exports.cloneUpdateQueue = cloneUpdateQueue;
function cloneUpdate(update: Update): Update {
return {
priorityLevel: update.priorityLevel,
partialState: update.partialState,
callback: update.callback,
isReplace: update.isReplace,
isForced: update.isForced,
isTopLevelUnmount: update.isTopLevelUnmount,
next: null,
};
}
function insertUpdateIntoQueue(
queue: UpdateQueue,
update: Update,
insertAfter: Update | null,
insertBefore: Update | null,
) {
if (insertAfter !== null) {
insertAfter.next = update;
} else {
// This is the first item in the queue.
update.next = queue.first;
queue.first = update;
}
if (insertBefore !== null) {
update.next = insertBefore;
} else {
// This is the last item in the queue.
queue.last = update;
}
}
// Returns the update after which the incoming update should be inserted into
// the queue, or null if it should be inserted at beginning.
function findInsertionPosition(queue, update): Update | null {
const priorityLevel = update.priorityLevel;
let insertAfter = null;
let insertBefore = null;
if (
queue.last !== null &&
comparePriority(queue.last.priorityLevel, priorityLevel) <= 0
) {
// Fast path for the common case where the update should be inserted at
// the end of the queue.
insertAfter = queue.last;
} else {
insertBefore = queue.first;
while (
insertBefore !== null &&
comparePriority(insertBefore.priorityLevel, priorityLevel) <= 0
) {
insertAfter = insertBefore;
insertBefore = insertBefore.next;
}
}
return insertAfter;
}
// The work-in-progress queue is a subset of the current queue (if it exists).
// We need to insert the incoming update into both lists. However, it's possible
// that the correct position in one list will be different from the position in
// the other. Consider the following case:
//
// Current: 3-5-6
// Work-in-progress: 6
//
// Then we receive an update with priority 4 and insert it into each list:
//
// Current: 3-4-5-6
// Work-in-progress: 4-6
//
// In the current queue, the new update's `next` pointer points to the update
// with priority 5. But in the work-in-progress queue, the pointer points to the
// update with priority 6. Because these two queues share the same persistent
// data structure, this won't do. (This can only happen when the incoming update
// has higher priority than all the updates in the work-in-progress queue.)
//
// To solve this, in the case where the incoming update needs to be inserted
// into two different positions, we'll make a clone of the update and insert
// each copy into a separate queue. This forks the list while maintaining a
// persistent structure, because the update that is added to the work-in-progress
// is always added to the front of the list.
//
// However, if incoming update is inserted into the same position of both lists,
// we shouldn't make a copy.
//
// If the update is cloned, it returns the cloned update.
function insertUpdate(fiber: Fiber, update: Update): Update | null {
const queue1 = ensureUpdateQueue(fiber);
const queue2 = fiber.alternate !== null
? ensureUpdateQueue(fiber.alternate)
: null;
// Warn if an update is scheduled from inside an updater function.
if (__DEV__) {
if (queue1.isProcessing || (queue2 !== null && queue2.isProcessing)) {
warning(
false,
'An update (setState, replaceState, or forceUpdate) was scheduled ' +
'from inside an update function. Update functions should be pure, ' +
'with zero side-effects. Consider using componentDidUpdate or a ' +
'callback.',
);
}
}
// Find the insertion position in the first queue.
const insertAfter1 = findInsertionPosition(queue1, update);
const insertBefore1 = insertAfter1 !== null
? insertAfter1.next
: queue1.first;
if (queue2 === null) {
// If there's no alternate queue, there's nothing else to do but insert.
insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1);
return null;
}
// If there is an alternate queue, find the insertion position.
const insertAfter2 = findInsertionPosition(queue2, update);
const insertBefore2 = insertAfter2 !== null
? insertAfter2.next
: queue2.first;
// Now we can insert into the first queue. This must come after finding both
// insertion positions because it mutates the list.
insertUpdateIntoQueue(queue1, update, insertAfter1, insertBefore1);
if (insertBefore1 !== insertBefore2) {
// The insertion positions are different, so we need to clone the update and
// insert the clone into the alternate queue.
const update2 = cloneUpdate(update);
insertUpdateIntoQueue(queue2, update2, insertAfter2, insertBefore2);
return update2;
} else {
// The insertion positions are the same, so when we inserted into the first
// queue, it also inserted into the alternate. All we need to do is update
// the alternate queue's `first` and `last` pointers, in case they
// have changed.
if (insertAfter2 === null) {
queue2.first = update;
}
if (insertBefore2 === null) {
queue2.last = null;
}
}
return null;
}
function addUpdate(
fiber: Fiber,
partialState: PartialState<any, any> | null,
callback: mixed,
priorityLevel: PriorityLevel,
): void {
const update = {
priorityLevel,
partialState,
callback,
isReplace: false,
isForced: false,
isTopLevelUnmount: false,
next: null,
};
insertUpdate(fiber, update);
}
exports.addUpdate = addUpdate;
function addReplaceUpdate(
fiber: Fiber,
state: any | null,
callback: Callback | null,
priorityLevel: PriorityLevel,
): void {
const update = {
priorityLevel,
partialState: state,
callback,
isReplace: true,
isForced: false,
isTopLevelUnmount: false,
next: null,
};
insertUpdate(fiber, update);
}
exports.addReplaceUpdate = addReplaceUpdate;
function addForceUpdate(
fiber: Fiber,
callback: Callback | null,
priorityLevel: PriorityLevel,
): void {
const update = {
priorityLevel,
partialState: null,
callback,
isReplace: false,
isForced: true,
isTopLevelUnmount: false,
next: null,
};
insertUpdate(fiber, update);
}
exports.addForceUpdate = addForceUpdate;
function getPendingPriority(queue: UpdateQueue): PriorityLevel {
return queue.first !== null ? queue.first.priorityLevel : NoWork;
}
exports.getPendingPriority = getPendingPriority;
function addTopLevelUpdate(
fiber: Fiber,
partialState: PartialState<any, any>,
callback: Callback | null,
priorityLevel: PriorityLevel,
): void {
const isTopLevelUnmount = partialState.element === null;
const update = {
priorityLevel,
partialState,
callback,
isReplace: false,
isForced: false,
isTopLevelUnmount,
next: null,
};
const update2 = insertUpdate(fiber, update);
if (isTopLevelUnmount) {
// Drop all updates that are lower-priority, so that the tree is not
// remounted. We need to do this for both queues.
const queue1 = fiber.updateQueue;
const queue2 = fiber.alternate !== null
? fiber.alternate.updateQueue
: null;
if (queue1 !== null && update.next !== null) {
update.next = null;
queue1.last = update;
}
if (queue2 !== null && update2 !== null && update2.next !== null) {
update2.next = null;
queue2.last = update;
}
}
}
exports.addTopLevelUpdate = addTopLevelUpdate;
function getStateFromUpdate(update, instance, prevState, props) {
const partialState = update.partialState;
if (typeof partialState === 'function') {
const updateFn = partialState;
return updateFn.call(instance, prevState, props);
} else {
return partialState;
}
}
function beginUpdateQueue(
workInProgress: Fiber,
queue: UpdateQueue,
instance: any,
prevState: any,
props: any,
priorityLevel: PriorityLevel,
): any {
if (__DEV__) {
// Set this flag so we can warn if setState is called inside the update
// function of another setState.
queue.isProcessing = true;
}
queue.hasForceUpdate = false;
// Applies updates with matching priority to the previous state to create
// a new state object.
let state = prevState;
let dontMutatePrevState = true;
let callbackList = null;
let update = queue.first;
while (
update !== null &&
comparePriority(update.priorityLevel, priorityLevel) <= 0
) {
// Remove each update from the queue right before it is processed. That way
// if setState is called from inside an updater function, the new update
// will be inserted in the correct position.
queue.first = update.next;
if (queue.first === null) {
queue.last = null;
}
let partialState;
if (update.isReplace) {
state = getStateFromUpdate(update, instance, state, props);
dontMutatePrevState = true;
} else {
partialState = getStateFromUpdate(update, instance, state, props);
if (partialState) {
if (dontMutatePrevState) {
state = Object.assign({}, state, partialState);
} else {
state = Object.assign(state, partialState);
}
dontMutatePrevState = false;
}
}
if (update.isForced) {
queue.hasForceUpdate = true;
}
// Second condition ignores top-level unmount callbacks if they are not the
// last update in the queue, since a subsequent update will cause a remount.
if (
update.callback !== null &&
!(update.isTopLevelUnmount && update.next !== null)
) {
callbackList = callbackList || [];
callbackList.push(update.callback);
workInProgress.effectTag |= CallbackEffect;
}
update = update.next;
}
queue.callbackList = callbackList;
if (queue.first === null && callbackList === null && !queue.hasForceUpdate) {
// The queue is empty and there are no callbacks. We can reset it.
workInProgress.updateQueue = null;
}
if (__DEV__) {
queue.isProcessing = false;
}
return state;
}
exports.beginUpdateQueue = beginUpdateQueue;
function commitCallbacks(
finishedWork: Fiber,
queue: UpdateQueue,
context: mixed,
) {
const callbackList = queue.callbackList;
if (callbackList === null) {
return;
}
for (let i = 0; i < callbackList.length; i++) {
const callback = callbackList[i];
invariant(
typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
callback,
);
callback.call(context);
}
}
exports.commitCallbacks = commitCallbacks;

Просмотреть файл

@ -1,25 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPriorityLevel
* @flow
*/
'use strict';
export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
module.exports = {
NoWork: 0, // No work is pending.
SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects.
TaskPriority: 2, // Completes at the end of the current tick.
AnimationPriority: 3, // Needs to complete before the next frame.
HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
LowPriority: 5, // Data fetching, or result from updating stores.
OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
};

Просмотреть файл

@ -1,27 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactTypeOfSideEffect
* @flow
*/
'use strict';
export type TypeOfSideEffect = number;
module.exports = {
NoEffect: 0, // 0b0000000
Placement: 1, // 0b0000001
Update: 2, // 0b0000010
PlacementAndUpdate: 3, // 0b0000011
Deletion: 4, // 0b0000100
ContentReset: 8, // 0b0001000
Callback: 16, // 0b0010000
Err: 32, // 0b0100000
Ref: 64, // 0b1000000
};

Просмотреть файл

@ -1,247 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactCoroutine;
var ReactFeatureFlags;
describe('ReactCoroutine', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('ReactNoop');
ReactCoroutine = require('ReactCoroutine');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
function div(...children) {
children = children.map(c => (typeof c === 'string' ? {text: c} : c));
return {type: 'div', children, prop: undefined};
}
function span(prop) {
return {type: 'span', children: [], prop};
}
it('should render a coroutine', () => {
var ops = [];
function Continuation({isSame}) {
ops.push(['Continuation', isSame]);
return <span prop={isSame ? 'foo==bar' : 'foo!=bar'} />;
}
// An alternative API could mark Continuation as something that needs
// yielding. E.g. Continuation.yieldType = 123;
function Child({bar}) {
ops.push(['Child', bar]);
return ReactCoroutine.createYield({
props: {
bar: bar,
},
continuation: Continuation,
});
}
function Indirection() {
ops.push('Indirection');
return [<Child key="a" bar={true} />, <Child key="b" bar={false} />];
}
function HandleYields(props, yields) {
ops.push('HandleYields');
return yields.map((y, i) => (
<y.continuation key={i} isSame={props.foo === y.props.bar} />
));
}
// An alternative API could mark Parent as something that needs
// yielding. E.g. Parent.handler = HandleYields;
function Parent(props) {
ops.push('Parent');
return ReactCoroutine.createCoroutine(
props.children,
HandleYields,
props,
);
}
function App() {
return <div><Parent foo={true}><Indirection /></Parent></div>;
}
ReactNoop.render(<App />);
ReactNoop.flush();
expect(ops).toEqual([
'Parent',
'Indirection',
['Child', true],
// Yield
['Child', false],
// Yield
'HandleYields',
// Continue yields
['Continuation', true],
['Continuation', false],
]);
expect(ReactNoop.getChildren()).toEqual([
div(span('foo==bar'), span('foo!=bar')),
]);
});
it('should update a coroutine', () => {
function Continuation({isSame}) {
return <span prop={isSame ? 'foo==bar' : 'foo!=bar'} />;
}
function Child({bar}) {
return ReactCoroutine.createYield({
props: {
bar: bar,
},
continuation: Continuation,
});
}
function Indirection() {
return [<Child key="a" bar={true} />, <Child key="b" bar={false} />];
}
function HandleYields(props, yields) {
return yields.map((y, i) => (
<y.continuation key={i} isSame={props.foo === y.props.bar} />
));
}
function Parent(props) {
return ReactCoroutine.createCoroutine(
props.children,
HandleYields,
props,
);
}
function App(props) {
return <div><Parent foo={props.foo}><Indirection /></Parent></div>;
}
ReactNoop.render(<App foo={true} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([
div(span('foo==bar'), span('foo!=bar')),
]);
ReactNoop.render(<App foo={false} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([
div(span('foo!=bar'), span('foo==bar')),
]);
});
it('should unmount a composite in a coroutine', () => {
var ops = [];
class Continuation extends React.Component {
render() {
ops.push('Continuation');
return <div />;
}
componentWillUnmount() {
ops.push('Unmount Continuation');
}
}
class Child extends React.Component {
render() {
ops.push('Child');
return ReactCoroutine.createYield(Continuation);
}
componentWillUnmount() {
ops.push('Unmount Child');
}
}
function HandleYields(props, yields) {
ops.push('HandleYields');
return yields.map((ContinuationComponent, i) => (
<ContinuationComponent key={i} />
));
}
class Parent extends React.Component {
render() {
ops.push('Parent');
return ReactCoroutine.createCoroutine(
this.props.children,
HandleYields,
this.props,
);
}
componentWillUnmount() {
ops.push('Unmount Parent');
}
}
ReactNoop.render(<Parent><Child /></Parent>);
ReactNoop.flush();
expect(ops).toEqual(['Parent', 'Child', 'HandleYields', 'Continuation']);
ops = [];
ReactNoop.render(<div />);
ReactNoop.flush();
expect(ops).toEqual([
'Unmount Parent',
'Unmount Child',
'Unmount Continuation',
]);
});
it('should handle deep updates in coroutine', () => {
let instances = {};
class Counter extends React.Component {
state = {value: 5};
render() {
instances[this.props.id] = this;
return ReactCoroutine.createYield(this.state.value);
}
}
function App(props) {
return ReactCoroutine.createCoroutine(
[
<Counter key="a" id="a" />,
<Counter key="b" id="b" />,
<Counter key="c" id="c" />,
],
(p, yields) => yields.map((y, i) => <span key={i} prop={y * 100} />),
{},
);
}
ReactNoop.render(<App />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(500), span(500), span(500)]);
instances.a.setState({value: 1});
instances.b.setState({value: 2});
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(100), span(200), span(500)]);
});
});

Просмотреть файл

@ -1,62 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactFiberReconciler;
describe('ReactFiberHostContext', () => {
beforeEach(() => {
jest.resetModules();
React = require('React');
ReactFiberReconciler = require('ReactFiberReconciler');
});
it('works with null host context', () => {
var creates = 0;
var Renderer = ReactFiberReconciler({
prepareForCommit: function() {},
resetAfterCommit: function() {},
getRootHostContext: function() {
return null;
},
getChildHostContext: function() {
return null;
},
shouldSetTextContent: function() {
return false;
},
createInstance: function() {
creates++;
},
finalizeInitialChildren: function() {
return null;
},
appendInitialChild: function() {
return null;
},
appendChild: function() {
return null;
},
useSyncScheduling: true,
});
const container = Renderer.createContainer(/* root: */ null);
Renderer.updateContainer(
<a><b /></a>,
container,
/* parentComponent: */ null,
/* callback: */ null,
);
expect(creates).toBe(2);
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,490 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
describe('ReactDebugFiberPerf', () => {
let React;
let ReactCoroutine;
let ReactFeatureFlags;
let ReactNoop;
let ReactPortal;
let PropTypes;
let root;
let activeMeasure;
let knownMarks;
let knownMeasures;
function resetFlamechart() {
root = {
children: [],
indent: -1,
markName: null,
label: null,
parent: null,
toString() {
return this.children.map(c => c.toString()).join('\n');
},
};
activeMeasure = root;
knownMarks = new Set();
knownMeasures = new Set();
}
function addComment(comment) {
activeMeasure.children.push(
`${' '.repeat(activeMeasure.indent + 1)}// ${comment}`,
);
}
function getFlameChart() {
// Make sure we unwind the measurement stack every time.
expect(activeMeasure.indent).toBe(-1);
expect(activeMeasure).toBe(root);
// We should always clean them up because browsers
// buffer user timing measurements forever.
expect(knownMarks.size).toBe(0);
expect(knownMeasures.size).toBe(0);
return root.toString();
}
function createUserTimingPolyfill() {
// This is not a true polyfill, but it gives us enough
// to capture measurements in a readable tree-like output.
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
return {
mark(markName) {
const measure = {
children: [],
indent: activeMeasure.indent + 1,
markName: markName,
// Will be assigned on measure() call:
label: null,
parent: activeMeasure,
toString() {
return (
[
' '.repeat(this.indent) + this.label,
...this.children.map(c => c.toString()),
].join('\n') +
// Extra newline after each root reconciliation
(this.indent === 0 ? '\n' : '')
);
},
};
// Step one level deeper
activeMeasure.children.push(measure);
activeMeasure = measure;
knownMarks.add(markName);
},
// We don't use the overload with three arguments.
measure(label, markName) {
if (markName !== activeMeasure.markName) {
throw new Error('Unexpected measure() call.');
}
// Step one level up
activeMeasure.label = label;
activeMeasure = activeMeasure.parent;
knownMeasures.add(label);
},
clearMarks(markName) {
if (markName === activeMeasure.markName) {
// Step one level up if we're in this measure
activeMeasure = activeMeasure.parent;
activeMeasure.children.length--;
}
knownMarks.delete(markName);
},
clearMeasures(label) {
knownMeasures.delete(label);
},
};
}
beforeEach(() => {
jest.resetModules();
resetFlamechart();
global.performance = createUserTimingPolyfill();
// Import after the polyfill is set up:
React = require('React');
ReactCoroutine = require('ReactCoroutine');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactNoop = require('ReactNoop');
ReactPortal = require('ReactPortal');
ReactFeatureFlags.disableNewFiberFeatures = false;
PropTypes = require('prop-types');
});
afterEach(() => {
delete global.performance;
});
function Parent(props) {
return <div>{props.children}</div>;
}
function Child(props) {
return <div>{props.children}</div>;
}
it('measures a simple reconciliation', () => {
ReactNoop.render(<Parent><Child /></Parent>);
addComment('Mount');
ReactNoop.flush();
ReactNoop.render(<Parent><Child /></Parent>);
addComment('Update');
ReactNoop.flush();
ReactNoop.render(null);
addComment('Unmount');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('skips parents during setState', () => {
class A extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
class B extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
let a;
let b;
ReactNoop.render(
<Parent>
<Parent>
<Parent>
<A ref={inst => (a = inst)} />
</Parent>
</Parent>
<Parent>
<B ref={inst => (b = inst)} />
</Parent>
</Parent>,
);
ReactNoop.flush();
resetFlamechart();
a.setState({});
b.setState({});
addComment('Should include just A and B, no Parents');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('warns on cascading renders from setState', () => {
class Cascading extends React.Component {
componentDidMount() {
this.setState({});
}
render() {
return <div>{this.props.children}</div>;
}
}
ReactNoop.render(<Parent><Cascading /></Parent>);
addComment('Should print a warning');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('warns on cascading renders from top-level render', () => {
class Cascading extends React.Component {
componentDidMount() {
ReactNoop.renderToRootWithID(<Child />, 'b');
addComment('Scheduling another root from componentDidMount');
ReactNoop.flush();
}
render() {
return <div>{this.props.children}</div>;
}
}
ReactNoop.renderToRootWithID(<Cascading />, 'a');
addComment('Rendering the first root');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('does not treat setState from cWM or cWRP as cascading', () => {
class NotCascading extends React.Component {
componentWillMount() {
this.setState({});
}
componentWillReceiveProps() {
this.setState({});
}
render() {
return <div>{this.props.children}</div>;
}
}
ReactNoop.render(<Parent><NotCascading /></Parent>);
addComment('Should not print a warning');
ReactNoop.flush();
ReactNoop.render(<Parent><NotCascading /></Parent>);
addComment('Should not print a warning');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('captures all lifecycles', () => {
class AllLifecycles extends React.Component {
static childContextTypes = {
foo: PropTypes.any,
};
shouldComponentUpdate() {
return true;
}
getChildContext() {
return {foo: 42};
}
componentWillMount() {}
componentDidMount() {}
componentWillReceiveProps() {}
componentWillUpdate() {}
componentDidUpdate() {}
componentWillUnmount() {}
render() {
return <div />;
}
}
ReactNoop.render(<AllLifecycles />);
addComment('Mount');
ReactNoop.flush();
ReactNoop.render(<AllLifecycles />);
addComment('Update');
ReactNoop.flush();
ReactNoop.render(null);
addComment('Unmount');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('measures deprioritized work', () => {
ReactNoop.performAnimationWork(() => {
ReactNoop.render(
<Parent>
<div hidden={true}>
<Child />
</div>
</Parent>,
);
});
addComment('Flush the parent');
ReactNoop.flushAnimationPri();
addComment('Flush the child');
ReactNoop.flushDeferredPri();
expect(getFlameChart()).toMatchSnapshot();
});
it('measures deferred work in chunks', () => {
class A extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
class B extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
ReactNoop.render(
<Parent>
<A>
<Child />
</A>
<B>
<Child />
</B>
</Parent>,
);
addComment('Start mounting Parent and A');
ReactNoop.flushDeferredPri(40);
addComment('Mount B just a little (but not enough to memoize)');
ReactNoop.flushDeferredPri(10);
addComment('Complete B and Parent');
ReactNoop.flushDeferredPri();
expect(getFlameChart()).toMatchSnapshot();
});
it('recovers from fatal errors', () => {
function Baddie() {
throw new Error('Game over');
}
ReactNoop.render(<Parent><Baddie /></Parent>);
try {
addComment('Will fatal');
ReactNoop.flush();
} catch (err) {
expect(err.message).toBe('Game over');
}
ReactNoop.render(<Parent><Child /></Parent>);
addComment('Will reconcile from a clean state');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('recovers from caught errors', () => {
function Baddie() {
throw new Error('Game over');
}
function ErrorReport() {
return <div />;
}
class Boundary extends React.Component {
state = {error: null};
unstable_handleError(error) {
this.setState({error});
}
render() {
if (this.state.error) {
return <ErrorReport />;
}
return this.props.children;
}
}
ReactNoop.render(
<Parent>
<Boundary>
<Parent>
<Baddie />
</Parent>
</Boundary>
</Parent>,
);
addComment('Stop on Baddie and restart from Boundary');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('deduplicates lifecycle names during commit to reduce overhead', () => {
class A extends React.Component {
componentDidUpdate() {}
render() {
return <div />;
}
}
class B extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.cascade && !prevProps.cascade) {
this.setState({});
}
}
render() {
return <div />;
}
}
ReactNoop.render(
<Parent>
<A />
<B />
<A />
<B />
</Parent>,
);
ReactNoop.flush();
resetFlamechart();
ReactNoop.render(
<Parent>
<A />
<B />
<A />
<B />
</Parent>,
);
addComment('The commit phase should mention A and B just once');
ReactNoop.flush();
ReactNoop.render(
<Parent>
<A />
<B />
<A />
<B cascade={true} />
</Parent>,
);
addComment("Because of deduplication, we don't know B was cascading,");
addComment('but we should still see the warning for the commit phase.');
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('supports coroutines', () => {
function Continuation({isSame}) {
return <span prop={isSame ? 'foo==bar' : 'foo!=bar'} />;
}
function CoChild({bar}) {
return ReactCoroutine.createYield({
props: {
bar: bar,
},
continuation: Continuation,
});
}
function Indirection() {
return [<CoChild key="a" bar={true} />, <CoChild key="b" bar={false} />];
}
function HandleYields(props, yields) {
return yields.map((y, i) => (
<y.continuation key={i} isSame={props.foo === y.props.bar} />
));
}
function CoParent(props) {
return ReactCoroutine.createCoroutine(
props.children,
HandleYields,
props,
);
}
function App() {
return <div><CoParent foo={true}><Indirection /></CoParent></div>;
}
ReactNoop.render(<App />);
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
it('supports portals', () => {
const noopContainer = {children: []};
ReactNoop.render(
<Parent>
{ReactPortal.createPortal(<Child />, noopContainer, null)}
</Parent>,
);
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
});

Просмотреть файл

@ -1,288 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactFeatureFlags;
describe('ReactIncrementalReflection', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('ReactNoop');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
it('handles isMounted even when the initial render is deferred', () => {
let ops = [];
const instances = [];
class Component extends React.Component {
_isMounted() {
// No longer a public API, but we can test that it works internally by
// reaching into the updater.
return this.updater.isMounted(this);
}
componentWillMount() {
instances.push(this);
ops.push('componentWillMount', this._isMounted());
}
componentDidMount() {
ops.push('componentDidMount', this._isMounted());
}
render() {
return <span />;
}
}
function Foo() {
return <Component />;
}
ReactNoop.render(<Foo />);
// Render part way through but don't yet commit the updates.
ReactNoop.flushDeferredPri(20);
expect(ops).toEqual(['componentWillMount', false]);
expect(instances[0]._isMounted()).toBe(false);
ops = [];
// Render the rest and commit the updates.
ReactNoop.flush();
expect(ops).toEqual(['componentDidMount', true]);
expect(instances[0]._isMounted()).toBe(true);
});
it('handles isMounted when an unmount is deferred', () => {
let ops = [];
const instances = [];
class Component extends React.Component {
_isMounted() {
return this.updater.isMounted(this);
}
componentWillMount() {
instances.push(this);
}
componentWillUnmount() {
ops.push('componentWillUnmount', this._isMounted());
}
render() {
ops.push('Component');
return <span />;
}
}
function Other() {
ops.push('Other');
return <span />;
}
function Foo(props) {
return props.mount ? <Component /> : <Other />;
}
ReactNoop.render(<Foo mount={true} />);
ReactNoop.flush();
expect(ops).toEqual(['Component']);
ops = [];
expect(instances[0]._isMounted()).toBe(true);
ReactNoop.render(<Foo mount={false} />);
// Render part way through but don't yet commit the updates so it is not
// fully unmounted yet.
ReactNoop.flushDeferredPri(20);
expect(ops).toEqual(['Other']);
ops = [];
expect(instances[0]._isMounted()).toBe(true);
// Finish flushing the unmount.
ReactNoop.flush();
expect(ops).toEqual(['componentWillUnmount', true]);
expect(instances[0]._isMounted()).toBe(false);
});
it('finds no node before insertion and correct node before deletion', () => {
let ops = [];
let classInstance = null;
class Component extends React.Component {
componentWillMount() {
classInstance = this;
ops.push('componentWillMount', ReactNoop.findInstance(this));
}
componentDidMount() {
ops.push('componentDidMount', ReactNoop.findInstance(this));
}
componentWillUpdate() {
ops.push('componentWillUpdate', ReactNoop.findInstance(this));
}
componentDidUpdate() {
ops.push('componentDidUpdate', ReactNoop.findInstance(this));
}
componentWillUnmount() {
ops.push('componentWillUnmount', ReactNoop.findInstance(this));
}
render() {
ops.push('render');
return this.props.step < 2
? <span ref={ref => (this.span = ref)} />
: this.props.step === 2
? <div ref={ref => (this.div = ref)} />
: this.props.step === 3
? null
: this.props.step === 4
? <div ref={ref => (this.span = ref)} />
: null;
}
}
function Sibling() {
// Sibling is used to assert that we've rendered past the first component.
ops.push('render sibling');
return <span />;
}
function Foo(props) {
return [<Component key="a" step={props.step} />, <Sibling key="b" />];
}
ReactNoop.render(<Foo step={0} />);
// Flush past Component but don't complete rendering everything yet.
ReactNoop.flushDeferredPri(30);
expect(ops).toEqual([
'componentWillMount',
null,
'render',
'render sibling',
]);
ops = [];
expect(classInstance).toBeDefined();
// The instance has been complete but is still not committed so it should
// not find any host nodes in it.
expect(ReactNoop.findInstance(classInstance)).toBe(null);
ReactNoop.flush();
const hostSpan = classInstance.span;
expect(hostSpan).toBeDefined();
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
expect(ops).toEqual(['componentDidMount', hostSpan]);
ops = [];
// Flush next step which will cause an update but not yet render a new host
// node.
ReactNoop.render(<Foo step={1} />);
ReactNoop.flush();
expect(ops).toEqual([
'componentWillUpdate',
hostSpan,
'render',
'render sibling',
'componentDidUpdate',
hostSpan,
]);
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
ops = [];
// The next step will render a new host node but won't get committed yet.
// We expect this to mutate the original Fiber.
ReactNoop.render(<Foo step={2} />);
ReactNoop.flushDeferredPri(30);
expect(ops).toEqual([
'componentWillUpdate',
hostSpan,
'render',
'render sibling',
]);
ops = [];
// This should still be the host span.
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
// When we finally flush the tree it will get committed.
ReactNoop.flush();
const hostDiv = classInstance.div;
expect(hostDiv).toBeDefined();
expect(hostSpan).not.toBe(hostDiv);
expect(ops).toEqual(['componentDidUpdate', hostDiv]);
ops = [];
// We should now find the new host node.
expect(ReactNoop.findInstance(classInstance)).toBe(hostDiv);
// Render to null but don't commit it yet.
ReactNoop.render(<Foo step={3} />);
ReactNoop.flushDeferredPri(25);
expect(ops).toEqual([
'componentWillUpdate',
hostDiv,
'render',
'render sibling',
]);
ops = [];
// This should still be the host div since the deletion is not committed.
expect(ReactNoop.findInstance(classInstance)).toBe(hostDiv);
ReactNoop.flush();
expect(ops).toEqual(['componentDidUpdate', null]);
// This should still be the host div since the deletion is not committed.
expect(ReactNoop.findInstance(classInstance)).toBe(null);
// Render a div again
ReactNoop.render(<Foo step={4} />);
ReactNoop.flush();
ops = [];
// Unmount the component.
ReactNoop.render([]);
ReactNoop.flush();
expect(ops).toEqual(['componentWillUnmount', hostDiv]);
});
});

Просмотреть файл

@ -1,409 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactFeatureFlags;
describe('ReactIncrementalScheduling', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('ReactNoop');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
function span(prop) {
return {type: 'span', children: [], prop};
}
it('schedules and flushes deferred work', () => {
ReactNoop.render(<span prop="1" />);
expect(ReactNoop.getChildren()).toEqual([]);
ReactNoop.flushDeferredPri();
expect(ReactNoop.getChildren()).toEqual([span('1')]);
});
it('schedules and flushes animation work', () => {
ReactNoop.performAnimationWork(() => {
ReactNoop.render(<span prop="1" />);
});
expect(ReactNoop.getChildren()).toEqual([]);
ReactNoop.flushAnimationPri();
expect(ReactNoop.getChildren()).toEqual([span('1')]);
});
it('searches for work on other roots once the current root completes', () => {
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
ReactNoop.flush();
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
});
it('schedules an animation callback when there`\s leftover animation work', () => {
class Foo extends React.Component {
state = {step: 0};
componentDidMount() {
ReactNoop.performAnimationWork(() => {
this.setState({step: 2});
});
this.setState({step: 1});
}
render() {
return <span prop={this.state.step} />;
}
}
ReactNoop.render(<Foo />);
// Flush just enough work to mount the component, but not enough to flush
// the animation update.
ReactNoop.flushDeferredPri(25);
expect(ReactNoop.getChildren()).toEqual([span(1)]);
// There's more animation work. A callback should have been scheduled.
ReactNoop.flushAnimationPri();
expect(ReactNoop.getChildren()).toEqual([span(2)]);
});
it('schedules top-level updates in order of priority', () => {
// Initial render.
ReactNoop.render(<span prop={1} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(1)]);
ReactNoop.render(<span prop={5} />);
ReactNoop.performAnimationWork(() => {
ReactNoop.render(<span prop={2} />);
ReactNoop.render(<span prop={3} />);
ReactNoop.render(<span prop={4} />);
});
// The low pri update should be flushed last, even though it was scheduled
// before the animation updates.
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(5)]);
});
it('schedules top-level updates with same priority in order of insertion', () => {
// Initial render.
ReactNoop.render(<span prop={1} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(1)]);
ReactNoop.render(<span prop={2} />);
ReactNoop.render(<span prop={3} />);
ReactNoop.render(<span prop={4} />);
ReactNoop.render(<span prop={5} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(5)]);
});
it('works on deferred roots in the order they were scheduled', () => {
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
ReactNoop.flush();
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
// Schedule deferred work in the reverse order
ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
// Ensure it starts in the order it was scheduled
ReactNoop.flushDeferredPri(15 + 5);
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
// Schedule last bit of work, it will get processed the last
ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
// Keep performing work in the order it was scheduled
ReactNoop.flushDeferredPri(15 + 5);
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
ReactNoop.flushDeferredPri(15 + 5);
expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
});
it('schedules sync updates when inside componentDidMount/Update', () => {
var instance;
var ops = [];
class Foo extends React.Component {
state = {tick: 0};
componentDidMount() {
ops.push('componentDidMount (before setState): ' + this.state.tick);
this.setState({tick: 1});
// We're in a batch. Update hasn't flushed yet.
ops.push('componentDidMount (after setState): ' + this.state.tick);
}
componentDidUpdate() {
ops.push('componentDidUpdate: ' + this.state.tick);
if (this.state.tick === 2) {
ops.push('componentDidUpdate (before setState): ' + this.state.tick);
this.setState({tick: 3});
ops.push('componentDidUpdate (after setState): ' + this.state.tick);
// We're in a batch. Update hasn't flushed yet.
}
}
render() {
ops.push('render: ' + this.state.tick);
instance = this;
return <span prop={this.state.tick} />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flushDeferredPri(20 + 5);
expect(ops).toEqual([
'render: 0',
'componentDidMount (before setState): 0',
'componentDidMount (after setState): 0',
// If the setState inside componentDidMount were deferred, there would be
// no more ops. Because it has Task priority, we get these ops, too:
'render: 1',
'componentDidUpdate: 1',
]);
ops = [];
instance.setState({tick: 2});
ReactNoop.flushDeferredPri(20 + 5);
expect(ops).toEqual([
'render: 2',
'componentDidUpdate: 2',
'componentDidUpdate (before setState): 2',
'componentDidUpdate (after setState): 2',
// If the setState inside componentDidUpdate were deferred, there would be
// no more ops. Because it has Task priority, we get these ops, too:
'render: 3',
'componentDidUpdate: 3',
]);
});
it('can opt-in to deferred/animation scheduling inside componentDidMount/Update', () => {
var instance;
var ops = [];
class Foo extends React.Component {
state = {tick: 0};
componentDidMount() {
ReactNoop.performAnimationWork(() => {
ops.push('componentDidMount (before setState): ' + this.state.tick);
this.setState({tick: 1});
ops.push('componentDidMount (after setState): ' + this.state.tick);
});
}
componentDidUpdate() {
ReactNoop.performAnimationWork(() => {
ops.push('componentDidUpdate: ' + this.state.tick);
if (this.state.tick === 2) {
ops.push(
'componentDidUpdate (before setState): ' + this.state.tick,
);
this.setState({tick: 3});
ops.push('componentDidUpdate (after setState): ' + this.state.tick);
}
});
}
render() {
ops.push('render: ' + this.state.tick);
instance = this;
return <span prop={this.state.tick} />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flushDeferredPri(20 + 5);
expect(ops).toEqual([
'render: 0',
'componentDidMount (before setState): 0',
'componentDidMount (after setState): 0',
// Following items shouldn't appear because they are the result of an
// update scheduled with animation priority
// 'render: 1',
// 'componentDidUpdate: 1',
]);
ops = [];
ReactNoop.flushAnimationPri();
expect(ops).toEqual(['render: 1', 'componentDidUpdate: 1']);
ops = [];
instance.setState({tick: 2});
ReactNoop.flushDeferredPri(20 + 5);
expect(ops).toEqual([
'render: 2',
'componentDidUpdate: 2',
'componentDidUpdate (before setState): 2',
'componentDidUpdate (after setState): 2',
// Following items shouldn't appear because they are the result of an
// update scheduled with animation priority
// 'render: 3',
// 'componentDidUpdate: 3',
]);
ops = [];
ReactNoop.flushAnimationPri();
expect(ops).toEqual(['render: 3', 'componentDidUpdate: 3']);
});
it('performs Task work even after time runs out', () => {
class Foo extends React.Component {
state = {step: 1};
componentDidMount() {
this.setState({step: 2}, () => {
this.setState({step: 3}, () => {
this.setState({step: 4}, () => {
this.setState({step: 5});
});
});
});
}
render() {
return <span prop={this.state.step} />;
}
}
ReactNoop.render(<Foo />);
// This should be just enough to complete all the work, but not enough to
// commit it.
ReactNoop.flushDeferredPri(20);
expect(ReactNoop.getChildren()).toEqual([]);
// Do one more unit of work.
ReactNoop.flushDeferredPri(10);
// The updates should all be flushed with Task priority
expect(ReactNoop.getChildren()).toEqual([span(5)]);
});
it('does not perform animation work after time runs out', () => {
class Foo extends React.Component {
state = {step: 1};
componentDidMount() {
ReactNoop.performAnimationWork(() => {
this.setState({step: 2}, () => {
this.setState({step: 3}, () => {
this.setState({step: 4}, () => {
this.setState({step: 5});
});
});
});
});
}
render() {
return <span prop={this.state.step} />;
}
}
ReactNoop.render(<Foo />);
// This should be just enough to complete all the work, but not enough to
// commit it.
ReactNoop.flushDeferredPri(20);
expect(ReactNoop.getChildren()).toEqual([]);
// Do one more unit of work.
ReactNoop.flushDeferredPri(10);
// None of the updates should be flushed because they only have
// animation priority.
expect(ReactNoop.getChildren()).toEqual([span(1)]);
});
it('can opt-out of batching using unbatchedUpdates', () => {
// syncUpdates gives synchronous priority to updates
ReactNoop.syncUpdates(() => {
// batchedUpdates downgrades sync updates to task priority
ReactNoop.batchedUpdates(() => {
ReactNoop.render(<span prop={0} />);
expect(ReactNoop.getChildren()).toEqual([]);
// Should not have flushed yet because we're still batching
// unbatchedUpdates reverses the effect of batchedUpdates, so sync
// updates are not batched
ReactNoop.unbatchedUpdates(() => {
ReactNoop.render(<span prop={1} />);
expect(ReactNoop.getChildren()).toEqual([span(1)]);
ReactNoop.render(<span prop={2} />);
expect(ReactNoop.getChildren()).toEqual([span(2)]);
});
ReactNoop.render(<span prop={3} />);
expect(ReactNoop.getChildren()).toEqual([span(2)]);
});
// Remaining update is now flushed
expect(ReactNoop.getChildren()).toEqual([span(3)]);
});
});
it('nested updates are always deferred, even inside unbatchedUpdates', () => {
let instance;
let ops = [];
class Foo extends React.Component {
state = {step: 0};
componentDidUpdate() {
ops.push('componentDidUpdate: ' + this.state.step);
if (this.state.step === 1) {
ReactNoop.unbatchedUpdates(() => {
// This is a nested state update, so it should not be
// flushed synchronously, even though we wrapped it
// in unbatchedUpdates.
this.setState({step: 2});
});
expect(ReactNoop.getChildren()).toEqual([span(1)]);
}
}
render() {
ops.push('render: ' + this.state.step);
instance = this;
return <span prop={this.state.step} />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([span(0)]);
ReactNoop.syncUpdates(() => {
instance.setState({step: 1});
expect(ReactNoop.getChildren()).toEqual([span(2)]);
});
expect(ops).toEqual([
'render: 0',
'render: 1',
'componentDidUpdate: 1',
'render: 2',
'componentDidUpdate: 2',
]);
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,376 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactFeatureFlags;
describe('ReactIncrementalUpdates', () => {
beforeEach(() => {
jest.resetModuleRegistry();
React = require('react');
ReactNoop = require('ReactNoop');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
it('applies updates in order of priority', () => {
let state;
class Foo extends React.Component {
state = {};
componentDidMount() {
ReactNoop.performAnimationWork(() => {
// Has Animation priority
this.setState({b: 'b'});
this.setState({c: 'c'});
});
// Has Task priority
this.setState({a: 'a'});
}
render() {
state = this.state;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flushDeferredPri(25);
expect(state).toEqual({a: 'a'});
ReactNoop.flush();
expect(state).toEqual({a: 'a', b: 'b', c: 'c'});
});
it('applies updates with equal priority in insertion order', () => {
let state;
class Foo extends React.Component {
state = {};
componentDidMount() {
// All have Task priority
this.setState({a: 'a'});
this.setState({b: 'b'});
this.setState({c: 'c'});
}
render() {
state = this.state;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
expect(state).toEqual({a: 'a', b: 'b', c: 'c'});
});
it('only drops updates with equal or lesser priority when replaceState is called', () => {
let instance;
let ops = [];
class Foo extends React.Component {
state = {};
componentDidMount() {
ops.push('componentDidMount');
}
componentDidUpdate() {
ops.push('componentDidUpdate');
}
render() {
ops.push('render');
instance = this;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
instance.setState({x: 'x'});
instance.setState({y: 'y'});
ReactNoop.performAnimationWork(() => {
instance.setState({a: 'a'});
instance.setState({b: 'b'});
});
instance.updater.enqueueReplaceState(instance, {c: 'c'});
instance.setState({d: 'd'});
ReactNoop.flushAnimationPri();
// Even though a replaceState has been already scheduled, it hasn't been
// flushed yet because it has low priority.
expect(instance.state).toEqual({a: 'a', b: 'b'});
expect(ops).toEqual([
'render',
'componentDidMount',
'render',
'componentDidUpdate',
]);
ops = [];
ReactNoop.flush();
// Now the rest of the updates are flushed.
expect(instance.state).toEqual({c: 'c', d: 'd'});
expect(ops).toEqual(['render', 'componentDidUpdate']);
});
it('can abort an update, schedule additional updates, and resume', () => {
let instance;
let ops = [];
class Foo extends React.Component {
state = {};
componentDidUpdate() {
ops.push('componentDidUpdate');
}
render() {
ops.push('render');
instance = this;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
ops = [];
let progressedUpdates = [];
function createUpdate(letter) {
return () => {
progressedUpdates.push(letter);
return {
[letter]: letter,
};
};
}
instance.setState(createUpdate('a'));
instance.setState(createUpdate('b'));
instance.setState(createUpdate('c'));
// Do just enough work to begin the update but not enough to flush it
ReactNoop.flushDeferredPri(15);
// expect(ReactNoop.getChildren()).toEqual([span('')]);
expect(ops).toEqual(['render']);
expect(progressedUpdates).toEqual(['a', 'b', 'c']);
expect(instance.state).toEqual({a: 'a', b: 'b', c: 'c'});
ops = [];
progressedUpdates = [];
instance.setState(createUpdate('f'));
ReactNoop.performAnimationWork(() => {
instance.setState(createUpdate('d'));
instance.setState(createUpdate('e'));
});
instance.setState(createUpdate('g'));
ReactNoop.flushAnimationPri();
expect(ops).toEqual([
// Flushes animation work (d and e)
'render',
'componentDidUpdate',
]);
ops = [];
ReactNoop.flush();
expect(ops).toEqual([
// Flushes deferred work (f and g)
'render',
'componentDidUpdate',
]);
expect(progressedUpdates).toEqual(['d', 'e', 'a', 'b', 'c', 'f', 'g']);
expect(instance.state).toEqual({
a: 'a',
b: 'b',
c: 'c',
d: 'd',
e: 'e',
f: 'f',
g: 'g',
});
});
it('can abort an update, schedule a replaceState, and resume', () => {
let instance;
let ops = [];
class Foo extends React.Component {
state = {};
componentDidUpdate() {
ops.push('componentDidUpdate');
}
render() {
ops.push('render');
instance = this;
return <span />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
ops = [];
let progressedUpdates = [];
function createUpdate(letter) {
return () => {
progressedUpdates.push(letter);
return {
[letter]: letter,
};
};
}
instance.setState(createUpdate('a'));
instance.setState(createUpdate('b'));
instance.setState(createUpdate('c'));
// Do just enough work to begin the update but not enough to flush it
ReactNoop.flushDeferredPri(20);
expect(ops).toEqual(['render']);
expect(progressedUpdates).toEqual(['a', 'b', 'c']);
expect(instance.state).toEqual({a: 'a', b: 'b', c: 'c'});
ops = [];
progressedUpdates = [];
instance.setState(createUpdate('f'));
ReactNoop.performAnimationWork(() => {
instance.setState(createUpdate('d'));
// No longer a public API, but we can test that it works internally by
// reaching into the updater.
instance.updater.enqueueReplaceState(instance, createUpdate('e'));
});
instance.setState(createUpdate('g'));
ReactNoop.flush();
expect(progressedUpdates).toEqual(['d', 'e', 'f', 'g']);
expect(instance.state).toEqual({e: 'e', f: 'f', g: 'g'});
});
it('passes accumulation of previous updates to replaceState updater function', () => {
let instance;
class Foo extends React.Component {
state = {};
render() {
instance = this;
return <span />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
instance.setState({a: 'a'});
instance.setState({b: 'b'});
// No longer a public API, but we can test that it works internally by
// reaching into the updater.
instance.updater.enqueueReplaceState(instance, previousState => ({
previousState,
}));
ReactNoop.flush();
expect(instance.state).toEqual({previousState: {a: 'a', b: 'b'}});
});
it('does not call callbacks that are scheduled by another callback until a later commit', () => {
let ops = [];
class Foo extends React.Component {
state = {};
componentDidMount() {
ops.push('did mount');
this.setState({a: 'a'}, () => {
ops.push('callback a');
this.setState({b: 'b'}, () => {
ops.push('callback b');
});
});
}
render() {
ops.push('render');
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
expect(ops).toEqual([
'render',
'did mount',
'render',
'callback a',
'render',
'callback b',
]);
});
it('gives setState during reconciliation the same priority as whatever level is currently reconciling', () => {
let instance;
let ops = [];
class Foo extends React.Component {
state = {};
componentWillReceiveProps() {
ops.push('componentWillReceiveProps');
this.setState({b: 'b'});
}
render() {
ops.push('render');
instance = this;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
ops = [];
ReactNoop.performAnimationWork(() => {
instance.setState({a: 'a'});
ReactNoop.render(<Foo />); // Trigger componentWillReceiveProps
});
ReactNoop.flush();
expect(instance.state).toEqual({a: 'a', b: 'b'});
expect(ops).toEqual(['componentWillReceiveProps', 'render']);
});
it('enqueues setState inside an updater function as if the in-progress update is progressed (and warns)', () => {
spyOn(console, 'error');
let instance;
let ops = [];
class Foo extends React.Component {
state = {};
render() {
ops.push('render');
instance = this;
return <div />;
}
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
instance.setState(function a() {
ops.push('setState updater');
this.setState({b: 'b'});
return {a: 'a'};
});
ReactNoop.flush();
expect(ops).toEqual([
// Initial render
'render',
'setState updater',
// Update b is enqueued with the same priority as update a, so it should
// be flushed in the same commit.
'render',
]);
expect(instance.state).toEqual({a: 'a', b: 'b'});
expectDev(console.error.calls.count()).toBe(1);
console.error.calls.reset();
});
});

Просмотреть файл

@ -1,163 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactFeatureFlags;
// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
describe('ReactTopLevelFragment', function() {
beforeEach(function() {
jest.resetModules();
React = require('react');
ReactNoop = require('ReactNoop');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
it('should render a simple fragment at the top of a component', function() {
function Fragment() {
return [<div key="a">Hello</div>, <div key="b">World</div>];
}
ReactNoop.render(<Fragment />);
ReactNoop.flush();
});
it('should preserve state when switching from a single child', function() {
var instance = null;
class Stateful extends React.Component {
render() {
instance = this;
return <div>Hello</div>;
}
}
function Fragment({condition}) {
return condition
? <Stateful key="a" />
: [<Stateful key="a" />, <div key="b">World</div>];
}
ReactNoop.render(<Fragment />);
ReactNoop.flush();
var instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
ReactNoop.flush();
var instanceB = instance;
expect(instanceB).toBe(instanceA);
});
it('should not preserve state when switching to a nested array', function() {
var instance = null;
class Stateful extends React.Component {
render() {
instance = this;
return <div>Hello</div>;
}
}
function Fragment({condition}) {
return condition
? <Stateful key="a" />
: [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
}
ReactNoop.render(<Fragment />);
ReactNoop.flush();
var instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
ReactNoop.flush();
var instanceB = instance;
expect(instanceB).not.toBe(instanceA);
});
it('preserves state if an implicit key slot switches from/to null', function() {
var instance = null;
class Stateful extends React.Component {
render() {
instance = this;
return <div>World</div>;
}
}
function Fragment({condition}) {
return condition
? [null, <Stateful key="a" />]
: [<div key="b">Hello</div>, <Stateful key="a" />];
}
ReactNoop.render(<Fragment />);
ReactNoop.flush();
var instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
ReactNoop.flush();
var instanceB = instance;
expect(instanceB).toBe(instanceA);
ReactNoop.render(<Fragment condition={false} />);
ReactNoop.flush();
var instanceC = instance;
expect(instanceC === instanceA).toBe(true);
});
it('should preserve state in a reorder', function() {
var instance = null;
class Stateful extends React.Component {
render() {
instance = this;
return <div>Hello</div>;
}
}
function Fragment({condition}) {
return condition
? [[<div key="b">World</div>, <Stateful key="a" />]]
: [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
}
ReactNoop.render(<Fragment />);
ReactNoop.flush();
var instanceA = instance;
expect(instanceA).not.toBe(null);
ReactNoop.render(<Fragment condition={true} />);
ReactNoop.flush();
var instanceB = instance;
expect(instanceB).toBe(instanceA);
});
});

Просмотреть файл

@ -1,42 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
var ReactFeatureFlags;
// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
describe('ReactTopLevelText', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactNoop = require('ReactNoop');
ReactFeatureFlags = require('ReactFeatureFlags');
ReactFeatureFlags.disableNewFiberFeatures = false;
});
it('should render a component returning strings directly from render', () => {
const Text = ({value}) => value;
ReactNoop.render(<Text value="foo" />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([{text: 'foo'}]);
});
it('should render a component returning numbers directly from render', () => {
const Text = ({value}) => value;
ReactNoop.render(<Text value={10} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([{text: '10'}]);
});
});

Просмотреть файл

@ -1,260 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReactDebugFiberPerf captures all lifecycles 1`] = `
"// Mount
⚛ (React Tree Reconciliation)
⚛ AllLifecycles [mount]
⚛ AllLifecycles.componentWillMount
⚛ AllLifecycles.getChildContext
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
⚛ AllLifecycles.componentDidMount
// Update
⚛ (React Tree Reconciliation)
⚛ AllLifecycles [update]
⚛ AllLifecycles.componentWillReceiveProps
⚛ AllLifecycles.shouldComponentUpdate
⚛ AllLifecycles.componentWillUpdate
⚛ AllLifecycles.getChildContext
⚛ (Committing Changes)
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 2 Total)
⚛ AllLifecycles.componentDidUpdate
// Unmount
⚛ (React Tree Reconciliation)
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ AllLifecycles.componentWillUnmount
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;
exports[`ReactDebugFiberPerf deduplicates lifecycle names during commit to reduce overhead 1`] = `
"// The commit phase should mention A and B just once
⚛ (React Tree Reconciliation)
⚛ Parent [update]
⚛ A [update]
⚛ B [update]
⚛ A [update]
⚛ B [update]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 9 Total)
⚛ (Calling Lifecycle Methods: 9 Total)
⚛ A.componentDidUpdate
⚛ B.componentDidUpdate
// Because of deduplication, we don't know B was cascading,
// but we should still see the warning for the commit phase.
⛔ (React Tree Reconciliation) Warning: There were cascading updates
⚛ Parent [update]
⚛ A [update]
⚛ B [update]
⚛ A [update]
⚛ B [update]
⛔ (Committing Changes) Warning: Lifecycle hook scheduled a cascading update
⚛ (Committing Host Effects: 9 Total)
⚛ (Calling Lifecycle Methods: 9 Total)
⚛ A.componentDidUpdate
⚛ B.componentDidUpdate
⚛ B [update]
⛔ (Committing Changes) Warning: Caused by a cascading update in earlier commit
⚛ (Committing Host Effects: 3 Total)
⚛ (Calling Lifecycle Methods: 3 Total)
⚛ B.componentDidUpdate
"
`;
exports[`ReactDebugFiberPerf does not treat setState from cWM or cWRP as cascading 1`] = `
"// Should not print a warning
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ NotCascading [mount]
⚛ NotCascading.componentWillMount
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
// Should not print a warning
⚛ (React Tree Reconciliation)
⚛ Parent [update]
⚛ NotCascading [update]
⚛ NotCascading.componentWillReceiveProps
⚛ (Committing Changes)
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 2 Total)
"
`;
exports[`ReactDebugFiberPerf measures a simple reconciliation 1`] = `
"// Mount
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Child [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
// Update
⚛ (React Tree Reconciliation)
⚛ Parent [update]
⚛ Child [update]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 2 Total)
// Unmount
⚛ (React Tree Reconciliation)
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;
exports[`ReactDebugFiberPerf measures deferred work in chunks 1`] = `
"// Start mounting Parent and A
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ A [mount]
⚛ Child [mount]
// Mount B just a little (but not enough to memoize)
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ B [mount]
// Complete B and Parent
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ B [mount]
⚛ Child [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;
exports[`ReactDebugFiberPerf measures deprioritized work 1`] = `
"// Flush the parent
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
// Flush the child
⚛ (React Tree Reconciliation)
⚛ Child [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 3 Total)
⚛ (Calling Lifecycle Methods: 2 Total)
"
`;
exports[`ReactDebugFiberPerf recovers from caught errors 1`] = `
"// Stop on Baddie and restart from Boundary
⛔ (React Tree Reconciliation) Warning: There were cascading updates
⚛ Parent [mount]
⚛ Boundary [mount]
⚛ Parent [mount]
⚛ Baddie [mount]
⛔ (Committing Changes) Warning: Lifecycle hook scheduled a cascading update
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
⚛ Boundary [update]
⚛ ErrorReport [mount]
⛔ (Committing Changes) Warning: Caused by a cascading update in earlier commit
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
"
`;
exports[`ReactDebugFiberPerf recovers from fatal errors 1`] = `
"// Will fatal
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Baddie [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
// Will reconcile from a clean state
⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Child [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;
exports[`ReactDebugFiberPerf skips parents during setState 1`] = `
"// Should include just A and B, no Parents
⚛ (React Tree Reconciliation)
⚛ A [update]
⚛ B [update]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 6 Total)
⚛ (Calling Lifecycle Methods: 6 Total)
"
`;
exports[`ReactDebugFiberPerf supports coroutines 1`] = `
"⚛ (React Tree Reconciliation)
⚛ App [mount]
⚛ CoParent [mount]
⚛ HandleYields [mount]
⚛ Indirection [mount]
⚛ CoChild [mount]
⚛ CoChild [mount]
⚛ Continuation [mount]
⚛ Continuation [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 3 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;
exports[`ReactDebugFiberPerf supports portals 1`] = `
"⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Child [mount]
⚛ (Committing Changes)
⚛ (Committing Host Effects: 3 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
"
`;
exports[`ReactDebugFiberPerf warns on cascading renders from setState 1`] = `
"// Should print a warning
⛔ (React Tree Reconciliation) Warning: There were cascading updates
⚛ Parent [mount]
⚛ Cascading [mount]
⛔ (Committing Changes) Warning: Lifecycle hook scheduled a cascading update
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
⛔ Cascading.componentDidMount Warning: Scheduled a cascading update
⚛ Cascading [update]
⛔ (Committing Changes) Warning: Caused by a cascading update in earlier commit
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 2 Total)
"
`;
exports[`ReactDebugFiberPerf warns on cascading renders from top-level render 1`] = `
"// Rendering the first root
⛔ (React Tree Reconciliation) Warning: There were cascading updates
⚛ Cascading [mount]
⛔ (Committing Changes) Warning: Lifecycle hook scheduled a cascading update
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
⛔ Cascading.componentDidMount Warning: Scheduled a cascading update
// Scheduling another root from componentDidMount
⚛ Child [mount]
⛔ (Committing Changes) Warning: Caused by a cascading update in earlier commit
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;

Просмотреть файл

@ -1,110 +0,0 @@
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactCoroutine
* @flow
*/
'use strict';
import type {ReactNodeList} from 'ReactTypes';
// The Symbol used to tag the special React types. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var REACT_COROUTINE_TYPE;
var REACT_YIELD_TYPE;
if (typeof Symbol === 'function' && Symbol.for) {
REACT_COROUTINE_TYPE = Symbol.for('react.coroutine');
REACT_YIELD_TYPE = Symbol.for('react.yield');
} else {
REACT_COROUTINE_TYPE = 0xeac8;
REACT_YIELD_TYPE = 0xeac9;
}
type CoroutineHandler<T> = (props: T, yields: Array<mixed>) => ReactNodeList;
export type ReactCoroutine = {
$$typeof: Symbol | number,
key: null | string,
children: any,
// This should be a more specific CoroutineHandler
handler: (props: any, yields: Array<mixed>) => ReactNodeList,
props: any,
};
export type ReactYield = {
$$typeof: Symbol | number,
value: mixed,
};
exports.createCoroutine = function<T>(
children: mixed,
handler: CoroutineHandler<T>,
props: T,
key: ?string = null,
): ReactCoroutine {
var coroutine = {
// This tag allow us to uniquely identify this as a React Coroutine
$$typeof: REACT_COROUTINE_TYPE,
key: key == null ? null : '' + key,
children: children,
handler: handler,
props: props,
};
if (__DEV__) {
// TODO: Add _store property for marking this as validated.
if (Object.freeze) {
Object.freeze(coroutine.props);
Object.freeze(coroutine);
}
}
return coroutine;
};
exports.createYield = function(value: mixed): ReactYield {
var yieldNode = {
// This tag allow us to uniquely identify this as a React Yield
$$typeof: REACT_YIELD_TYPE,
value: value,
};
if (__DEV__) {
// TODO: Add _store property for marking this as validated.
if (Object.freeze) {
Object.freeze(yieldNode);
}
}
return yieldNode;
};
/**
* Verifies the object is a coroutine object.
*/
exports.isCoroutine = function(object: mixed): boolean {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_COROUTINE_TYPE
);
};
/**
* Verifies the object is a yield object.
*/
exports.isYield = function(object: mixed): boolean {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_YIELD_TYPE
);
};
exports.REACT_YIELD_TYPE = REACT_YIELD_TYPE;
exports.REACT_COROUTINE_TYPE = REACT_COROUTINE_TYPE;

Просмотреть файл

@ -1,60 +0,0 @@
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPortal
* @flow
*/
'use strict';
import type {ReactNodeList} from 'ReactTypes';
// The Symbol used to tag the special React types. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var REACT_PORTAL_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.portal')) ||
0xeaca;
export type ReactPortal = {
$$typeof: Symbol | number,
key: null | string,
containerInfo: any,
children: ReactNodeList,
// TODO: figure out the API for cross-renderer implementation.
implementation: any,
};
exports.createPortal = function(
children: ReactNodeList,
containerInfo: any,
// TODO: figure out the API for cross-renderer implementation.
implementation: any,
key: ?string = null,
): ReactPortal {
return {
// This tag allow us to uniquely identify this as a React Portal
$$typeof: REACT_PORTAL_TYPE,
key: key == null ? null : '' + key,
children,
containerInfo,
implementation,
};
};
/**
* Verifies the object is a portal object.
*/
exports.isPortal = function(object: mixed): boolean {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_PORTAL_TYPE
);
};
exports.REACT_PORTAL_TYPE = REACT_PORTAL_TYPE;

Просмотреть файл

@ -1,54 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactHostOperationHistoryHook
* @flow
*/
'use strict';
import type {DebugID} from 'ReactInstanceType';
export type Operation = {instanceID: DebugID} & (
| {type: 'mount', payload: string}
| {type: 'insert child', payload: {toIndex: number, content: string}}
| {type: 'move child', payload: {fromIndex: number, toIndex: number}}
| {type: 'replace children', payload: string}
| {type: 'replace text', payload: string}
| {type: 'replace with', payload: string}
| {type: 'update styles', payload: mixed /* Style Object */}
| {type: 'update attribute', payload: {[name: string]: string}}
| {type: 'remove attribute', payload: string});
// Trust the developer to only use this with a __DEV__ check
var ReactHostOperationHistoryHook = ((null: any): typeof ReactHostOperationHistoryHook);
if (__DEV__) {
var history: Array<Operation> = [];
ReactHostOperationHistoryHook = {
onHostOperation(operation: Operation) {
history.push(operation);
},
clearHistory(): void {
if (ReactHostOperationHistoryHook._preventClearing) {
// Should only be used for tests.
return;
}
history = [];
},
getHistory(): Array<Operation> {
return history;
},
};
}
module.exports = ReactHostOperationHistoryHook;

Просмотреть файл

@ -1,42 +0,0 @@
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactInvalidSetStateWarningHook
* @flow
*/
'use strict';
var warning = require('fbjs/lib/warning');
var ReactInvalidSetStateWarningHook = {};
if (__DEV__) {
var processingChildContext = false;
var warnInvalidSetState = function() {
warning(
!processingChildContext,
'setState(...): Cannot call setState() inside getChildContext()',
);
};
ReactInvalidSetStateWarningHook = {
onBeginProcessingChildContext(): void {
processingChildContext = true;
},
onEndProcessingChildContext(): void {
processingChildContext = false;
},
onSetState(): void {
warnInvalidSetState();
},
};
}
module.exports = ReactInvalidSetStateWarningHook;

Просмотреть файл

@ -1,45 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactInstanceMap
*/
'use strict';
/**
* `ReactInstanceMap` maintains a mapping from a public facing stateful
* instance (key) and the internal representation (value). This allows public
* methods to accept the user facing instance as an argument and map them back
* to internal methods.
*/
// TODO: Replace this with ES6: var ReactInstanceMap = new Map();
var ReactInstanceMap = {
/**
* This API should be called `delete` but we'd have to make sure to always
* transform these to strings for IE support. When this transform is fully
* supported we can rename it.
*/
remove: function(key) {
key._reactInternalInstance = undefined;
},
get: function(key) {
return key._reactInternalInstance;
},
has: function(key) {
return key._reactInternalInstance !== undefined;
},
set: function(key, value) {
key._reactInternalInstance = value;
},
};
module.exports = ReactInstanceMap;

Просмотреть файл

@ -1,146 +0,0 @@
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactTreeTraversal
*/
'use strict';
var {HostComponent} = require('ReactTypeOfWork');
function getParent(inst) {
if (inst._hostParent !== undefined) {
return inst._hostParent;
}
if (typeof inst.tag === 'number') {
do {
inst = inst.return;
// TODO: If this is a HostRoot we might want to bail out.
// That is depending on if we want nested subtrees (layers) to bubble
// events to their parent. We could also go through parentNode on the
// host node but that wouldn't work for React Native and doesn't let us
// do the portal feature.
} while (inst && inst.tag !== HostComponent);
if (inst) {
return inst;
}
}
return null;
}
/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
*/
function getLowestCommonAncestor(instA, instB) {
var depthA = 0;
for (var tempA = instA; tempA; tempA = getParent(tempA)) {
depthA++;
}
var depthB = 0;
for (var tempB = instB; tempB; tempB = getParent(tempB)) {
depthB++;
}
// If A is deeper, crawl up.
while (depthA - depthB > 0) {
instA = getParent(instA);
depthA--;
}
// If B is deeper, crawl up.
while (depthB - depthA > 0) {
instB = getParent(instB);
depthB--;
}
// Walk in lockstep until we find a match.
var depth = depthA;
while (depth--) {
if (instA === instB || instA === instB.alternate) {
return instA;
}
instA = getParent(instA);
instB = getParent(instB);
}
return null;
}
/**
* Return if A is an ancestor of B.
*/
function isAncestor(instA, instB) {
while (instB) {
if (instA === instB || instA === instB.alternate) {
return true;
}
instB = getParent(instB);
}
return false;
}
/**
* Return the parent instance of the passed-in instance.
*/
function getParentInstance(inst) {
return getParent(inst);
}
/**
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
*/
function traverseTwoPhase(inst, fn, arg) {
var path = [];
while (inst) {
path.push(inst);
inst = getParent(inst);
}
var i;
for (i = path.length; i-- > 0; ) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
fn(path[i], 'bubbled', arg);
}
}
/**
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
* should would receive a `mouseEnter` or `mouseLeave` event.
*
* Does not invoke the callback on the nearest common ancestor because nothing
* "entered" or "left" that element.
*/
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
var common = from && to ? getLowestCommonAncestor(from, to) : null;
var pathFrom = [];
while (from && from !== common) {
pathFrom.push(from);
from = getParent(from);
}
var pathTo = [];
while (to && to !== common) {
pathTo.push(to);
to = getParent(to);
}
var i;
for (i = 0; i < pathFrom.length; i++) {
fn(pathFrom[i], 'bubbled', argFrom);
}
for (i = pathTo.length; i-- > 0; ) {
fn(pathTo[i], 'captured', argTo);
}
}
module.exports = {
isAncestor: isAncestor,
getLowestCommonAncestor: getLowestCommonAncestor,
getParentInstance: getParentInstance,
traverseTwoPhase: traverseTwoPhase,
traverseEnterLeave: traverseEnterLeave,
};

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше