React sync (16 beta 6 plus addons)
Reviewed By: sebmarkbage Differential Revision: D4775005 fbshipit-source-id: c50d099dc3d01c10e122c56f11bd990a2b1e81d1
This commit is contained in:
parent
92d985fb49
commit
6f9447e7b2
|
@ -11,4 +11,4 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
module.exports = '16.0.0-alpha.4';
|
||||
module.exports = '16.0.0-alpha.6';
|
||||
|
|
|
@ -102,11 +102,6 @@ var ReactNativeEventEmitter = {
|
|||
) {
|
||||
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
|
||||
var inst = ReactNativeComponentTree.getInstanceFromNode(rootNodeID);
|
||||
if (!inst) {
|
||||
// If the original instance is already gone, we don't have to dispatch
|
||||
// any events.
|
||||
return;
|
||||
}
|
||||
ReactGenericBatching.batchedUpdates(function() {
|
||||
ReactNativeEventEmitter.handleTopLevel(
|
||||
topLevelType,
|
||||
|
|
|
@ -267,3 +267,97 @@ it('handles when a responder is unmounted while a touch sequence is in progress'
|
|||
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',
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -12,8 +12,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
import type {CapturedError} from 'ReactFiberScheduler';
|
||||
|
||||
let showDialog = emptyFunction;
|
||||
|
||||
function logCapturedError(capturedError: CapturedError): void {
|
||||
if (__DEV__) {
|
||||
const {
|
||||
|
@ -80,6 +85,22 @@ function logCapturedError(capturedError: CapturedError): void {
|
|||
`React caught an error thrown by one of your components.\n\n${error.stack}`,
|
||||
);
|
||||
}
|
||||
|
||||
showDialog(capturedError);
|
||||
}
|
||||
|
||||
exports.injection = {
|
||||
injectDialog(fn: (e: CapturedError) => void) {
|
||||
invariant(
|
||||
showDialog === emptyFunction,
|
||||
'The custom dialog was already injected.',
|
||||
);
|
||||
invariant(
|
||||
typeof fn === 'function',
|
||||
'Injected showDialog() must be a function.',
|
||||
);
|
||||
showDialog = fn;
|
||||
},
|
||||
};
|
||||
|
||||
exports.logCapturedError = logCapturedError;
|
||||
|
|
|
@ -12,8 +12,70 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
let caughtError = null;
|
||||
|
||||
let invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) {
|
||||
const funcArgs = Array.prototype.slice.call(arguments, 3);
|
||||
try {
|
||||
func.apply(context, funcArgs);
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
/**
|
||||
* To help development we can get better devtools integration by simulating a
|
||||
* real browser event.
|
||||
*/
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
typeof window.dispatchEvent === 'function' &&
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.createEvent === 'function'
|
||||
) {
|
||||
const fakeNode = document.createElement('react');
|
||||
let depth = 0;
|
||||
|
||||
invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) {
|
||||
depth++;
|
||||
const thisDepth = depth;
|
||||
const funcArgs = Array.prototype.slice.call(arguments, 3);
|
||||
const boundFunc = function() {
|
||||
func.apply(context, funcArgs);
|
||||
};
|
||||
let fakeEventError = null;
|
||||
const onFakeEventError = function(event) {
|
||||
// Don't capture nested errors
|
||||
if (depth === thisDepth) {
|
||||
fakeEventError = event.error;
|
||||
}
|
||||
};
|
||||
const evtType = `react-${name ? name : 'invokeguardedcallback'}-${depth}`;
|
||||
window.addEventListener('error', onFakeEventError);
|
||||
fakeNode.addEventListener(evtType, boundFunc, false);
|
||||
const evt = document.createEvent('Event');
|
||||
evt.initEvent(evtType, false, false);
|
||||
fakeNode.dispatchEvent(evt);
|
||||
fakeNode.removeEventListener(evtType, boundFunc, false);
|
||||
window.removeEventListener('error', onFakeEventError);
|
||||
depth--;
|
||||
return fakeEventError;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let rethrowCaughtError = function() {
|
||||
if (caughtError) {
|
||||
const error = caughtError;
|
||||
caughtError = null;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call a function while guarding against errors that happens within it.
|
||||
* Returns an error if it throws, otherwise null.
|
||||
|
@ -24,6 +86,16 @@ let caughtError = null;
|
|||
* @param {...*} args Arguments for function
|
||||
*/
|
||||
const ReactErrorUtils = {
|
||||
injection: {
|
||||
injectErrorUtils(injectedErrorUtils: Object) {
|
||||
invariant(
|
||||
typeof injectedErrorUtils.invokeGuardedCallback === 'function',
|
||||
'Injected invokeGuardedCallback() must be a function.',
|
||||
);
|
||||
invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback;
|
||||
},
|
||||
},
|
||||
|
||||
invokeGuardedCallback: function<A, B, C, D, E, F, Context>(
|
||||
name: string | null,
|
||||
func: (a: A, b: B, c: C, d: D, e: E, f: F) => void,
|
||||
|
@ -35,13 +107,7 @@ const ReactErrorUtils = {
|
|||
e: E,
|
||||
f: F,
|
||||
): Error | null {
|
||||
const funcArgs = Array.prototype.slice.call(arguments, 3);
|
||||
try {
|
||||
func.apply(context, funcArgs);
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
return null;
|
||||
return invokeGuardedCallback.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -75,64 +141,8 @@ const ReactErrorUtils = {
|
|||
* we will rethrow to be handled by the top level error handler.
|
||||
*/
|
||||
rethrowCaughtError: function() {
|
||||
if (caughtError) {
|
||||
const error = caughtError;
|
||||
caughtError = null;
|
||||
throw error;
|
||||
}
|
||||
return rethrowCaughtError.apply(this, arguments);
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
/**
|
||||
* To help development we can get better devtools integration by simulating a
|
||||
* real browser event.
|
||||
*/
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
typeof window.dispatchEvent === 'function' &&
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.createEvent === 'function'
|
||||
) {
|
||||
const fakeNode = document.createElement('react');
|
||||
let depth = 0;
|
||||
|
||||
ReactErrorUtils.invokeGuardedCallback = function(
|
||||
name,
|
||||
func,
|
||||
context,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
) {
|
||||
depth++;
|
||||
const thisDepth = depth;
|
||||
const funcArgs = Array.prototype.slice.call(arguments, 3);
|
||||
const boundFunc = function() {
|
||||
func.apply(context, funcArgs);
|
||||
};
|
||||
let fakeEventError = null;
|
||||
const onFakeEventError = function(event) {
|
||||
// Don't capture nested errors
|
||||
if (depth === thisDepth) {
|
||||
fakeEventError = event.error;
|
||||
}
|
||||
};
|
||||
const evtType = `react-${name ? name : 'invokeguardedcallback'}-${depth}`;
|
||||
window.addEventListener('error', onFakeEventError);
|
||||
fakeNode.addEventListener(evtType, boundFunc, false);
|
||||
const evt = document.createEvent('Event');
|
||||
evt.initEvent(evtType, false, false);
|
||||
fakeNode.dispatchEvent(evt);
|
||||
fakeNode.removeEventListener(evtType, boundFunc, false);
|
||||
window.removeEventListener('error', onFakeEventError);
|
||||
depth--;
|
||||
return fakeEventError;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactErrorUtils;
|
||||
|
|
11
package.json
11
package.json
|
@ -128,7 +128,7 @@
|
|||
"react-native": "local-cli/wrong-react-native.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "~16.0.0-alpha.4"
|
||||
"react": "16.0.0-alpha.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"absolute-path": "^0.0.0",
|
||||
|
@ -187,6 +187,9 @@
|
|||
"plist": "^1.2.0",
|
||||
"pretty-format": "^4.2.1",
|
||||
"promise": "^7.1.1",
|
||||
"react-addons-create-fragment": "15.5.0-alpha.0",
|
||||
"react-addons-pure-render-mixin": "15.5.0-alpha.0",
|
||||
"react-addons-shallow-compare": "15.5.0-alpha.0",
|
||||
"react-clone-referenced-element": "^1.0.1",
|
||||
"react-devtools-core": "^2.0.8",
|
||||
"react-timer-mixin": "^0.13.2",
|
||||
|
@ -224,9 +227,9 @@
|
|||
"jest-repl": "19.0.2",
|
||||
"jest-runtime": "19.0.2",
|
||||
"mock-fs": "^3.11.0",
|
||||
"react": "~16.0.0-alpha.4",
|
||||
"react-dom": "~16.0.0-alpha.4",
|
||||
"react-test-renderer": "~16.0.0-alpha.3",
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-dom": "16.0.0-alpha.6",
|
||||
"react-test-renderer": "16.0.0-alpha.6",
|
||||
"shelljs": "0.6.0",
|
||||
"sinon": "^2.0.0-pre.2"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue