Sync React Native with React 16.0.0-alpha.3

Reviewed By: sebmarkbage

Differential Revision: D4615201

fbshipit-source-id: fc62b44aa866e096faa1cb048db361bb8c6ab11f
This commit is contained in:
Andrew Clark 2017-02-27 13:08:37 -08:00 committed by Facebook Github Bot
parent 2d44908afa
commit 24a7665df5
24 changed files with 6652 additions and 257 deletions

View File

@ -77,11 +77,11 @@ class Animated {
throw new Error('This node cannot be made a "native" animated node');
}
}
__getNativeTag(): number {
__getNativeTag(): ?number {
NativeAnimatedHelper.assertNativeAnimatedModule();
invariant(this.__isNative, 'Attempt to get native tag from node not marked as "native"');
if (this.__nativeTag == null) {
var nativeTag: number = NativeAnimatedHelper.generateNewNodeTag();
var nativeTag: ?number = NativeAnimatedHelper.generateNewNodeTag();
NativeAnimatedAPI.createAnimatedNode(nativeTag, this.__getNativeConfig());
this.__nativeTag = nativeTag;
}

View File

@ -23,7 +23,7 @@ type EndResult = {finished: boolean};
type EndCallback = (result: EndResult) => void;
type EventMapping = {
nativeEventPath: Array<string>,
animatedValueTag: number,
animatedValueTag: ?number,
};
let nativeEventEmitter;
@ -33,67 +33,67 @@ let nativeEventEmitter;
* the native module methods
*/
const API = {
createAnimatedNode: function(tag: number, config: Object): void {
createAnimatedNode: function(tag: ?number, config: Object): void {
assertNativeAnimatedModule();
NativeAnimatedModule.createAnimatedNode(tag, config);
},
startListeningToAnimatedNodeValue: function(tag: number) {
startListeningToAnimatedNodeValue: function(tag: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag);
},
stopListeningToAnimatedNodeValue: function(tag: number) {
stopListeningToAnimatedNodeValue: function(tag: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag);
},
connectAnimatedNodes: function(parentTag: number, childTag: number): void {
connectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag);
},
disconnectAnimatedNodes: function(parentTag: number, childTag: number): void {
disconnectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag);
},
startAnimatingNode: function(animationId: number, nodeTag: number, config: Object, endCallback: EndCallback): void {
startAnimatingNode: function(animationId: ?number, nodeTag: ?number, config: Object, endCallback: EndCallback): void {
assertNativeAnimatedModule();
NativeAnimatedModule.startAnimatingNode(animationId, nodeTag, config, endCallback);
},
stopAnimation: function(animationId: number) {
stopAnimation: function(animationId: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.stopAnimation(animationId);
},
setAnimatedNodeValue: function(nodeTag: number, value: number): void {
setAnimatedNodeValue: function(nodeTag: ?number, value: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value);
},
setAnimatedNodeOffset: function(nodeTag: number, offset: number): void {
setAnimatedNodeOffset: function(nodeTag: ?number, offset: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset);
},
flattenAnimatedNodeOffset: function(nodeTag: number): void {
flattenAnimatedNodeOffset: function(nodeTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag);
},
extractAnimatedNodeOffset: function(nodeTag: number): void {
extractAnimatedNodeOffset: function(nodeTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag);
},
connectAnimatedNodeToView: function(nodeTag: number, viewTag: number): void {
connectAnimatedNodeToView: function(nodeTag: ?number, viewTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag);
},
disconnectAnimatedNodeFromView: function(nodeTag: number, viewTag: number): void {
disconnectAnimatedNodeFromView: function(nodeTag: ?number, viewTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag);
},
dropAnimatedNode: function(tag: number): void {
dropAnimatedNode: function(tag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.dropAnimatedNode(tag);
},
addAnimatedEventToView: function(viewTag: number, eventName: string, eventMapping: EventMapping) {
addAnimatedEventToView: function(viewTag: ?number, eventName: string, eventMapping: EventMapping) {
assertNativeAnimatedModule();
NativeAnimatedModule.addAnimatedEventToView(viewTag, eventName, eventMapping);
},
removeAnimatedEventFromView(viewTag: number, eventName: string) {
removeAnimatedEventFromView(viewTag: ?number, eventName: string) {
assertNativeAnimatedModule();
NativeAnimatedModule.removeAnimatedEventFromView(viewTag, eventName);
}

View File

@ -11,4 +11,4 @@
'use strict';
module.exports = '16.0.0-alpha.2';
module.exports = '16.0.0-alpha.3';

View File

@ -53,6 +53,7 @@ function uncacheNode(inst) {
function uncacheFiberNode(tag) {
delete instanceCache[tag];
delete instanceProps[tag];
}
function getInstanceFromTag(tag) {
@ -61,7 +62,9 @@ function getInstanceFromTag(tag) {
function getTagFromInstance(inst) {
// TODO (bvaughn) Clean up once Stack is deprecated
var tag = inst._rootNodeID || inst.stateNode._nativeTag;
var tag = typeof inst.tag !== 'number'
? inst._rootNodeID
: inst.stateNode._nativeTag;
invariant(tag, 'All native instances should have a tag.');
return tag;
}

View File

@ -96,11 +96,7 @@ const NativeRenderer = ReactFiberReconciler({
},
appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
if (typeof child === 'number') {
parentInstance._children.push(child);
} else {
parentInstance._children.push(child);
}
parentInstance._children.push(child);
},
commitTextUpdate(

View File

@ -16,10 +16,10 @@ var ReactNativeGlobalResponderHandler = {
onChange: function(from, to, blockNativeResponder) {
if (to !== null) {
// TODO (bvaughn) Clean up once Stack is deprecated
UIManager.setJSResponder(
to._rootNodeID || to.stateNode._nativeTag,
blockNativeResponder
);
var tag = typeof to.tag !== 'number'
? to._rootNodeID
: to.stateNode._nativeTag;
UIManager.setJSResponder(tag, blockNativeResponder);
} else {
UIManager.clearJSResponder();
}

View File

@ -15,6 +15,7 @@ var RCTEventEmitter;
var React;
var ReactErrorUtils;
var ReactNative;
var ResponderEventPlugin;
var UIManager;
var createReactNativeComponentClass;
@ -25,6 +26,7 @@ beforeEach(() => {
React = require('React');
ReactErrorUtils = require('ReactErrorUtils');
ReactNative = require('ReactNative');
ResponderEventPlugin = require('ResponderEventPlugin');
UIManager = require('UIManager');
createReactNativeComponentClass = require('createReactNativeComponentClass');
@ -91,3 +93,97 @@ it('handles events', () => {
'outer 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']);
});

View File

@ -27,13 +27,13 @@ type ReactNativeBaseComponentViewConfig = {
* @param {string} config iOS View configuration.
* @private
*/
const createReactNativeFiberComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig
): ReactClass<any> {
// TODO(sema): This actually returns a string. Need to fix this before
// we deploy Fiber.
return (ReactNativeViewConfigRegistry.register(viewConfig) : any);
};
const createReactNativeFiberComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig
): ReactClass<any> {
// TODO(sema): This actually returns a string. Need to fix this before
// we deploy Fiber.
return (ReactNativeViewConfigRegistry.register(viewConfig) : any);
};
/**
* @param {string} config iOS View configuration.
@ -59,6 +59,6 @@ const createReactNativeComponentClass = function(
return ((Constructor: any): ReactClass<any>);
};
module.exports = (ReactNativeFeatureFlags.useFiber
module.exports = ReactNativeFeatureFlags.useFiber
? createReactNativeFiberComponentClass
: createReactNativeComponentClass);
: createReactNativeComponentClass;

View File

@ -53,8 +53,7 @@ import type { ReactInstance } from 'ReactInstanceType';
let injectedFindNode;
let injectedFindRootNodeID;
// TODO(sema): Type this more specifically.
function findNodeHandle(componentOrHandle: any): any {
function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
// TODO: fix this unsafe cast to work with Fiber.
var owner = ((ReactCurrentOwner.current: any): ReactInstance | null);

View File

@ -223,8 +223,12 @@ var createFiber = function(tag : TypeOfWork, key : null | string) : Fiber {
fiber._debugID = debugCounter++;
fiber._debugSource = null;
fiber._debugOwner = null;
if (typeof Object.preventExtensions === 'function') {
Object.preventExtensions(fiber);
}
}
return fiber;
};

View File

@ -67,7 +67,6 @@ var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
var warning = require('fbjs/lib/warning');
var warnedAboutStatelessRefs = {};
}
@ -479,6 +478,15 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
// 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();

View File

@ -291,6 +291,7 @@ module.exports = function(
instance.props = props;
instance.state = state;
instance.refs = emptyObject;
instance.context = getMaskedContext(workInProgress, unmaskedContext);
if (typeof instance.componentWillMount === 'function') {
@ -408,6 +409,19 @@ module.exports = function(
if (oldProps !== newProps || oldContext !== newContext) {
if (typeof instance.componentWillReceiveProps === 'function') {
instance.componentWillReceiveProps(newProps, newContext);
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);
}
}
}

View File

@ -34,6 +34,7 @@ const {
if (__DEV__) {
var checkReactTypeSpec = require('checkReactTypeSpec');
var ReactDebugCurrentFrame = require('react/lib/ReactDebugCurrentFrame');
var warnedAboutMissingGetChildContext = {};
}
@ -91,7 +92,9 @@ exports.getMaskedContext = function(workInProgress : Fiber, unmaskedContext : Ob
if (__DEV__) {
const name = getComponentName(workInProgress);
checkReactTypeSpec(contextTypes, context, 'context', name, null, workInProgress);
ReactDebugCurrentFrame.current = workInProgress;
checkReactTypeSpec(contextTypes, context, 'context', name);
ReactDebugCurrentFrame.current = null;
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
@ -182,7 +185,9 @@ function processChildContext(fiber : Fiber, parentContext : Object, isReconcilin
// 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;
checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, workInProgress);
ReactDebugCurrentFrame.current = workInProgress;
checkReactTypeSpec(childContextTypes, childContext, 'child context', name);
ReactDebugCurrentFrame.current = null;
}
return {...parentContext, ...childContext};
}

View File

@ -12,15 +12,13 @@
'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;
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
let rendererID = null;
let injectInternals = null;
let onCommitRoot = null;

View File

@ -21,6 +21,7 @@ export type CapturedError = {
componentName : ?string,
componentStack : string,
error : Error,
errorBoundary : ?Object,
errorBoundaryFound : boolean,
errorBoundaryName : string | null,
willRetry : boolean,
@ -86,8 +87,21 @@ var {
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
var warnAboutUpdateOnUnmounted = function(instance : ReactClass<any>) {
const ctor = instance.constructor;
warning(
false,
'Can only update a mounted or mounting component. This usually means ' +
'you called setState, replaceState, or forceUpdate on an unmounted ' +
'component. This is a no-op.\n\nPlease check the code for the ' +
'%s component.',
ctor && (ctor.displayName || ctor.name) || 'ReactClass'
);
};
}
var timeHeuristicForUnitOfWork = 1;
@ -349,6 +363,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
'in React. Please file an issue.'
);
// Reset this to null before calling lifecycles
ReactCurrentOwner.current = null;
// Updates that occur during the commit phase should have Task priority
const previousPriorityContext = priorityContext;
priorityContext = TaskPriority;
@ -941,6 +958,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
componentName,
componentStack,
error,
errorBoundary: errorBoundaryFound ? boundary.stateNode : null,
errorBoundaryFound,
errorBoundaryName,
willRetry,
@ -1129,7 +1147,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
return;
}
} else {
// TODO: Warn about setting state on an unmounted component.
if (__DEV__) {
if (fiber.tag === ClassComponent) {
warnAboutUpdateOnUnmounted(fiber.stateNode);
}
}
return;
}
}

View File

@ -15,13 +15,19 @@
import type { Fiber } from 'ReactFiber';
var ReactInstanceMap = require('ReactInstanceMap');
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
var {
HostRoot,
HostComponent,
HostText,
ClassComponent,
} = require('ReactTypeOfWork');
var {
@ -66,6 +72,24 @@ exports.isFiberMounted = function(fiber : Fiber) : boolean {
};
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)
);
instance._warnedAboutRefsInRender = true;
}
}
var fiber : ?Fiber = ReactInstanceMap.get(component);
if (!fiber) {
return false;
@ -243,7 +267,7 @@ exports.findCurrentHostFiber = function(parent : Fiber) : Fiber | null {
return null;
};
exports.getComponentName = function(fiber: Fiber): string {
function getComponentName(fiber: Fiber): string {
const type = fiber.type;
const instance = fiber.stateNode;
const constructor = instance && instance.constructor;
@ -252,4 +276,5 @@ exports.getComponentName = function(fiber: Fiber): string {
type.name || (constructor && constructor.name) ||
'A Component'
);
};
}
exports.getComponentName = getComponentName;

View File

@ -24,6 +24,7 @@ var ReactReconciler = require('ReactReconciler');
if (__DEV__) {
var checkReactTypeSpec = require('checkReactTypeSpec');
var ReactDebugCurrentFrame = require('react/lib/ReactDebugCurrentFrame');
var warningAboutMissingGetChildContext = {};
}
@ -33,8 +34,6 @@ var shallowEqual = require('fbjs/lib/shallowEqual');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
var warning = require('fbjs/lib/warning');
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
@ -687,7 +686,7 @@ var ReactCompositeComponent = {
this._checkContextTypes(
Component.childContextTypes,
childContext,
'childContext'
'child context'
);
}
for (var name in childContext) {
@ -730,17 +729,17 @@ var ReactCompositeComponent = {
_checkContextTypes: function(
typeSpecs,
values,
location: ReactPropTypeLocations,
location: string,
) {
if (__DEV__) {
ReactDebugCurrentFrame.current = this._debugID;
checkReactTypeSpec(
typeSpecs,
values,
location,
this.getName(),
null,
this._debugID
this.getName()
);
ReactDebugCurrentFrame.current = null;
}
},
@ -850,6 +849,7 @@ var ReactCompositeComponent = {
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (willReceive && inst.componentWillReceiveProps) {
const beforeState = inst.state;
if (__DEV__) {
measureLifeCyclePerf(
() => inst.componentWillReceiveProps(nextProps, nextContext),
@ -859,6 +859,20 @@ var ReactCompositeComponent = {
} else {
inst.componentWillReceiveProps(nextProps, nextContext);
}
const afterState = inst.state;
if (beforeState !== afterState) {
inst.state = beforeState;
inst.updater.enqueueReplaceState(inst, afterState);
if (__DEV__) {
warning(
false,
'%s.componentWillReceiveProps(): Assigning directly to ' +
'this.state is deprecated (except inside a component\'s ' +
'constructor). Use setState instead.',
this.getName() || 'ReactCompositeComponent'
);
}
}
}
// If updating happens to enqueue any new updates, we shouldn't execute new

View File

@ -38,16 +38,12 @@ function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
if (!internalInstance) {
if (__DEV__) {
var ctor = publicInstance.constructor;
// Only warn when we have a callerName. Otherwise we should be silent.
// We're probably calling from enqueueCallback. We don't want to warn
// there because we already warned for the corresponding lifecycle method.
warning(
!callerName,
'%s(...): Can only update a mounted or mounting component. ' +
'This usually means you called %s() on an unmounted component. ' +
'This is a no-op.\n\nPlease check the code for the %s component.',
callerName,
callerName,
false,
'Can only update a mounted or mounting component. This usually means ' +
'you called setState, replaceState, or forceUpdate on an unmounted ' +
'component. This is a no-op.\n\nPlease check the code for the ' +
'%s component.',
ctor && (ctor.displayName || ctor.name) || 'ReactClass'
);
}
@ -57,12 +53,10 @@ function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
if (__DEV__) {
warning(
ReactCurrentOwner.current == null,
'%s(...): Cannot update during an existing state transition (such as ' +
'within `render` or another component\'s constructor). Render methods ' +
'should be a pure function of props and state; constructor ' +
'side-effects are an anti-pattern, but can be moved to ' +
'`componentWillMount`.',
callerName
'Cannot update during an existing state transition (such as within ' +
'`render` or another component\'s constructor). Render methods should ' +
'be a pure function of props and state; constructor side-effects are ' +
'an anti-pattern, but can be moved to `componentWillMount`.',
);
}
@ -134,10 +128,7 @@ var ReactUpdateQueue = {
* @internal
*/
enqueueForceUpdate: function(publicInstance, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(
publicInstance,
'forceUpdate'
);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
@ -174,10 +165,7 @@ var ReactUpdateQueue = {
* @internal
*/
enqueueReplaceState: function(publicInstance, completeState, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(
publicInstance,
'replaceState'
);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
@ -223,10 +211,7 @@ var ReactUpdateQueue = {
);
}
var internalInstance = getInternalInstanceReadyForUpdate(
publicInstance,
'setState'
);
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;

View File

@ -1,29 +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.
*
* @flow
* @providesModule ReactPropTypeLocationNames
*/
'use strict';
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
type NamesType = {[key: ReactPropTypeLocations]: string};
var ReactPropTypeLocationNames: NamesType = {};
if (__DEV__) {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context',
};
}
module.exports = ReactPropTypeLocationNames;

View File

@ -1,18 +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.
*
* @flow
* @providesModule ReactPropTypeLocations
*/
'use strict';
export type ReactPropTypeLocations =
'prop' |
'context' |
'childContext';

View File

@ -11,124 +11,22 @@
'use strict';
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('ReactPropTypesSecret');
var checkPropTypes = require('react/lib/checkPropTypes');
var { getStackAddendum } = require('react/lib/ReactDebugCurrentFrame');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
import type { ReactPropTypeLocations } from 'ReactPropTypeLocations';
var ReactComponentTreeHook;
if (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'test'
) {
// Temporary hack.
// Inline requires don't work well with Jest:
// https://github.com/facebook/react/issues/7240
// Remove the inline requires when we don't need them anymore:
// https://github.com/facebook/react/pull/7178
ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
}
var loggedTypeFailures = {};
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
*
* @param {object} typeSpecs Map of name to a ReactPropType
* @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @param {string} componentName Name of the component for error messages.
* @param {?object} element The React element that is being type-checked
* @param {?number} workInProgressOrDebugID The React component instance that is being type-checked
* @private
*/
function checkReactTypeSpec(
typeSpecs,
values,
location: ReactPropTypeLocations,
componentName,
element,
// It is only safe to pass fiber if it is the work-in-progress version, and
// only during reconciliation (begin and complete phase).
workInProgressOrDebugID,
location: string,
componentName
) {
for (var typeSpecName in typeSpecs) {
if (typeSpecs.hasOwnProperty(typeSpecName)) {
var error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
invariant(
typeof typeSpecs[typeSpecName] === 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
componentName || 'React class',
ReactPropTypeLocationNames[location],
typeSpecName
);
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
} catch (ex) {
error = ex;
}
warning(
!error || error instanceof Error,
'%s: type specification of %s `%s` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a %s. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).',
componentName || 'React class',
ReactPropTypeLocationNames[location],
typeSpecName,
typeof error
);
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
var componentStackInfo = '';
if (__DEV__) {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
}
if (workInProgressOrDebugID != null) {
if (typeof workInProgressOrDebugID === 'number') {
// DebugID from Stack.
const debugID = workInProgressOrDebugID;
componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
} else if (typeof workInProgressOrDebugID.tag === 'number') {
// This is a Fiber.
// The stack will only be correct if this is a work in progress
// version and we're calling it during reconciliation.
const workInProgress = workInProgressOrDebugID;
componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
}
} else if (element !== null) {
componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
}
}
warning(
false,
'Failed %s type: %s%s',
location,
error.message,
componentStackInfo
);
}
}
}
checkPropTypes(
typeSpecs,
values,
location,
componentName,
getStackAddendum
);
}
module.exports = checkReactTypeSpec;

File diff suppressed because it is too large Load Diff

View File

@ -10,17 +10,14 @@
*/
'use strict';
var ReactPropTypeLocationNames = require('react/lib/ReactPropTypeLocationNames');
var normalizeColor = require('normalizeColor');
var colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
var color = props[propName];
if (color === undefined || color === null) {
if (isRequired) {
var locationName = ReactPropTypeLocationNames[location];
return new Error(
'Required ' + locationName + ' `' + (propFullName || propName) +
'Required ' + location + ' `' + (propFullName || propName) +
'` was not specified in `' + componentName + '`.'
);
}
@ -35,9 +32,8 @@ var colorPropType = function(isRequired, props, propName, componentName, locatio
}
if (normalizeColor(color) === null) {
var locationName = ReactPropTypeLocationNames[location];
return new Error(
'Invalid ' + locationName + ' `' + (propFullName || propName) +
'Invalid ' + location + ' `' + (propFullName || propName) +
'` supplied to `' + componentName + '`: ' + color + '\n' +
`Valid color formats are
- '#f0f' (#rgb)

View File

@ -127,7 +127,7 @@
"react-native": "local-cli/wrong-react-native.js"
},
"peerDependencies": {
"react": "16.0.0-alpha.2"
"react": "~16.0.0-alpha.3"
},
"dependencies": {
"absolute-path": "^0.0.0",
@ -159,7 +159,7 @@
"debug": "^2.2.0",
"denodeify": "^1.2.1",
"event-target-shim": "^1.0.5",
"fbjs": "^0.8.9",
"fbjs": "~0.8.9",
"fbjs-scripts": "^0.7.0",
"form-data": "^2.1.1",
"fs-extra": "^0.26.2",
@ -222,9 +222,9 @@
"jest-repl": "19.0.2",
"jest-runtime": "19.0.2",
"mock-fs": "^3.11.0",
"react": "16.0.0-alpha.2",
"react-dom": "16.0.0-alpha.2",
"react-test-renderer": "16.0.0-alpha.2",
"react": "~16.0.0-alpha.3",
"react-dom": "~16.0.0-alpha.3",
"react-test-renderer": "~16.0.0-alpha.3",
"shelljs": "0.6.0",
"sinon": "^2.0.0-pre.2"
}