Sync React Native with React 16.0.0-alpha.3 (take 2)
Reviewed By: spicyj Differential Revision: D4623242 fbshipit-source-id: 7ddb057cb47e005dda73070f45d108af40d93c8f
This commit is contained in:
parent
6439f65fcb
commit
41f1bcc5ac
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -11,4 +11,4 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
module.exports = '16.0.0-alpha.2';
|
||||
module.exports = '16.0.0-alpha.3';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
|
|
|
@ -27,14 +27,14 @@ 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.
|
||||
* @private
|
||||
|
@ -59,6 +59,6 @@ const createReactNativeComponentClass = function(
|
|||
return ((Constructor: any): ReactClass<any>);
|
||||
};
|
||||
|
||||
module.exports = (ReactNativeFeatureFlags.useFiber
|
||||
module.exports = ReactNativeFeatureFlags.useFiber
|
||||
? createReactNativeFiberComponentClass
|
||||
: createReactNativeComponentClass);
|
||||
: createReactNativeComponentClass;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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';
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
10
package.json
10
package.json
|
@ -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"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue