Compare commits

...

3 Commits

Author SHA1 Message Date
Brian Vaughn
c3718c48f0 16 beta 5 version bump and results JSON 2017-08-08 10:25:25 -07:00
Brian Vaughn
1724b116cd Enable new fiber ReactTestRenderer API methods (#10410)
Enable new fiber ReactTestRenderer API methods
2017-08-08 10:17:59 -07:00
Andrew Clark
5cdd744e06 Fix bugs related to unmounting error boundaries (#10403)
* Don't warn about setState on unmounted when scheduling error recovery

We shouldn't schedule an update on unmounted error boundaries, but we
don't know if a boundary is unmounted until we traverse its parents.
Added an additional argument to scheduleUpdate so we know not to warn
about setState on unmounted components.

* Should be able to unmount an error boundary before it is handled

Fixes the case where an error boundary captures an error, but its
parent is unmounted before we can re-render it. componentDidCatch is
never called, and we don't remove the boundary from our set of
unhandled error boundaries.

We should not assume that if capturedErrors is non-null that we still
have unhandled errors.
2017-08-08 10:09:43 -07:00
14 changed files with 182 additions and 105 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "react-build",
"private": true,
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"devDependencies": {
"aliasify": "^2.0.0",
"art": "^0.10.1",

View File

@@ -1,6 +1,6 @@
{
"name": "react-art",
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
"main": "index.js",
"repository": "facebook/react",
@@ -23,7 +23,7 @@
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^16.0.0-beta.4"
"react": "^16.0.0-beta.5"
},
"files": [
"LICENSE",

View File

@@ -1,6 +1,6 @@
{
"name": "react-dom",
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"description": "React package for working with the DOM.",
"main": "index.js",
"repository": "facebook/react",
@@ -19,7 +19,7 @@
"prop-types": "^15.5.6"
},
"peerDependencies": {
"react": "^16.0.0-beta.4"
"react": "^16.0.0-beta.5"
},
"files": [
"LICENSE",

View File

@@ -1,6 +1,6 @@
{
"name": "react-noop-renderer",
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"private": true,
"description": "React package for testing the Fiber reconciler.",
"main": "index.js",

View File

@@ -1,6 +1,6 @@
{
"name": "react-test-renderer",
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"description": "React package for snapshot testing.",
"main": "index.js",
"repository": "facebook/react",
@@ -19,7 +19,7 @@
"object-assign": "^4.1.0"
},
"peerDependencies": {
"react": "^16.0.0-beta.4"
"react": "^16.0.0-beta.5"
},
"files": [
"LICENSE",

View File

@@ -1,7 +1,7 @@
{
"name": "react",
"description": "React is a JavaScript library for building user interfaces.",
"version": "16.0.0-beta.4",
"version": "16.0.0-beta.5",
"keywords": [
"react"
],

View File

@@ -221,5 +221,9 @@
"219": "getInspectorDataForViewTag() is not available in production",
"220": "Container does not support insertBefore operation",
"221": "Tried to register two views with the same name %s",
"222": "View config not found for name %s"
"222": "View config not found for name %s",
"223": "Trying to release an event instance into a pool of a different type.",
"224": "Can't read from currently-mounting component. This error is likely caused by a bug in React. Please file an issue.",
"225": "Unexpected object passed to ReactTestInstance constructor (tag: %s). This is probably a bug in React.",
"226": "Unsupported component type %s in test renderer. This is probably a bug in React."
}

View File

@@ -18,59 +18,59 @@
},
"React-dev.js (FB_DEV)": {
"size": 53503,
"gzip": 13659
"gzip": 13660
},
"React-prod.js (FB_PROD)": {
"size": 25014,
"gzip": 6703
},
"react-dom.development.js (UMD_DEV)": {
"size": 634231,
"gzip": 144541
"size": 634292,
"gzip": 144575
},
"react-dom.production.min.js (UMD_PROD)": {
"size": 119426,
"gzip": 38138
"size": 119041,
"gzip": 37995
},
"react-dom.development.js (NODE_DEV)": {
"size": 593640,
"gzip": 134958
"size": 593703,
"gzip": 134999
},
"react-dom.production.min.js (NODE_PROD)": {
"size": 116342,
"gzip": 37065
"size": 116323,
"gzip": 37033
},
"ReactDOMFiber-dev.js (FB_DEV)": {
"size": 590470,
"gzip": 134456
"size": 590523,
"gzip": 134461
},
"ReactDOMFiber-prod.js (FB_PROD)": {
"size": 424217,
"gzip": 94997
"size": 423907,
"gzip": 94912
},
"react-dom-test-utils.development.js (NODE_DEV)": {
"size": 53312,
"gzip": 13394
"size": 53332,
"gzip": 13396
},
"ReactTestUtils-dev.js (FB_DEV)": {
"size": 53124,
"gzip": 13357
},
"react-dom-unstable-native-dependencies.development.js (UMD_DEV)": {
"size": 88255,
"gzip": 22340
"size": 88275,
"gzip": 22341
},
"react-dom-unstable-native-dependencies.production.min.js (UMD_PROD)": {
"size": 18178,
"gzip": 5939
"size": 17748,
"gzip": 5783
},
"react-dom-unstable-native-dependencies.development.js (NODE_DEV)": {
"size": 81703,
"gzip": 20376
"size": 81723,
"gzip": 20379
},
"react-dom-unstable-native-dependencies.production.min.js (NODE_PROD)": {
"size": 16448,
"gzip": 5300
"size": 16380,
"gzip": 5252
},
"ReactDOMUnstableNativeDependencies-dev.js (FB_DEV)": {
"size": 81422,
@@ -102,7 +102,7 @@
},
"ReactDOMServer-prod.js (FB_PROD)": {
"size": 49636,
"gzip": 13861
"gzip": 13862
},
"react-dom-server.node.development.js (NODE_DEV)": {
"size": 94782,
@@ -113,28 +113,28 @@
"gzip": 7964
},
"react-art.development.js (UMD_DEV)": {
"size": 373477,
"gzip": 82691
"size": 373518,
"gzip": 82706
},
"react-art.production.min.js (UMD_PROD)": {
"size": 93070,
"gzip": 28782
"size": 93115,
"gzip": 28802
},
"react-art.development.js (NODE_DEV)": {
"size": 294864,
"gzip": 62680
"size": 294907,
"gzip": 62698
},
"react-art.production.min.js (NODE_PROD)": {
"size": 55010,
"gzip": 16977
"size": 55055,
"gzip": 16989
},
"ReactARTFiber-dev.js (FB_DEV)": {
"size": 293795,
"gzip": 62752
"size": 293848,
"gzip": 62755
},
"ReactARTFiber-prod.js (FB_PROD)": {
"size": 219555,
"gzip": 45520
"size": 219245,
"gzip": 45443
},
"ReactNativeStack-dev.js (RN_DEV)": {
"size": 201024,
@@ -145,20 +145,20 @@
"gzip": 26481
},
"ReactNativeFiber-dev.js (RN_DEV)": {
"size": 306159,
"gzip": 53035
"size": 306175,
"gzip": 53018
},
"ReactNativeFiber-prod.js (RN_PROD)": {
"size": 222136,
"gzip": 38567
"size": 222113,
"gzip": 38548
},
"react-test-renderer.development.js (NODE_DEV)": {
"size": 299936,
"gzip": 63221
"size": 299343,
"gzip": 63142
},
"ReactTestRendererFiber-dev.js (FB_DEV)": {
"size": 298876,
"gzip": 63302
"size": 298269,
"gzip": 63216
},
"react-test-renderer-shallow.development.js (NODE_DEV)": {
"size": 9556,
@@ -169,8 +169,8 @@
"gzip": 2338
},
"react-noop-renderer.development.js (NODE_DEV)": {
"size": 286575,
"gzip": 59988
"size": 286618,
"gzip": 60007
}
}
}

View File

@@ -11,4 +11,4 @@
'use strict';
module.exports = '16.0.0-beta.4';
module.exports = '16.0.0-beta.5';

View File

@@ -761,7 +761,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
// The loop stops once the children have unmounted and error lifecycles are
// called. Then we return to the regular flow.
if (capturedErrors !== null && capturedErrors.size > 0) {
if (
capturedErrors !== null &&
capturedErrors.size > 0 &&
nextPriorityLevel === TaskPriority
) {
while (nextUnitOfWork !== null) {
if (hasCapturedError(nextUnitOfWork)) {
// Use a forked version of performUnitOfWork
@@ -780,19 +784,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
commitAllWork(pendingCommit);
priorityContext = nextPriorityLevel;
if (capturedErrors === null || capturedErrors.size === 0) {
if (
capturedErrors === null ||
capturedErrors.size === 0 ||
nextPriorityLevel !== TaskPriority
) {
// There are no more unhandled errors. We can exit this special
// work loop. If there's still additional work, we'll perform it
// using one of the normal work loops.
break;
}
// The commit phase produced additional errors. Continue working.
invariant(
nextPriorityLevel === TaskPriority,
'Commit phase errors should be scheduled to recover with task ' +
'priority. This error is likely caused by a bug in React. ' +
'Please file an issue.',
);
}
}
}
@@ -1129,7 +1131,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
willRetry = true;
}
} else if (node.tag === HostRoot) {
// Treat the root like a no-op error boundary.
// Treat the root like a no-op error boundary
boundary = node;
}
@@ -1349,6 +1351,14 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
}
function scheduleUpdate(fiber: Fiber, priorityLevel: PriorityLevel) {
return scheduleUpdateImpl(fiber, priorityLevel, false);
}
function scheduleUpdateImpl(
fiber: Fiber,
priorityLevel: PriorityLevel,
isErrorRecovery: boolean,
) {
if (__DEV__) {
recordScheduleUpdate();
}
@@ -1372,7 +1382,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
}
if (__DEV__) {
if (fiber.tag === ClassComponent) {
if (!isErrorRecovery && fiber.tag === ClassComponent) {
const instance = fiber.stateNode;
warnAboutInvalidUpdates(instance);
}
@@ -1438,7 +1448,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
}
} else {
if (__DEV__) {
if (fiber.tag === ClassComponent) {
if (!isErrorRecovery && fiber.tag === ClassComponent) {
warnAboutUpdateOnUnmounted(fiber.stateNode);
}
}
@@ -1478,7 +1488,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
}
function scheduleErrorRecovery(fiber: Fiber) {
scheduleUpdate(fiber, TaskPriority);
scheduleUpdateImpl(fiber, TaskPriority, true);
}
function performWithPriority(priorityLevel: PriorityLevel, fn: Function) {

View File

@@ -177,11 +177,7 @@ describe('ReactIncrementalErrorHandling', () => {
}
ReactNoop.flushSync(() => {
ReactNoop.render(
<ErrorBoundary>
Before the storm.
</ErrorBoundary>,
);
ReactNoop.render(<ErrorBoundary>Before the storm.</ErrorBoundary>);
ReactNoop.render(
<ErrorBoundary>
<BrokenRender />
@@ -327,9 +323,7 @@ describe('ReactIncrementalErrorHandling', () => {
expect(() => {
ReactNoop.flushSync(() => {
ReactNoop.render(
<RethrowErrorBoundary>
Before the storm.
</RethrowErrorBoundary>,
<RethrowErrorBoundary>Before the storm.</RethrowErrorBoundary>,
);
ReactNoop.render(
<RethrowErrorBoundary>
@@ -478,6 +472,82 @@ describe('ReactIncrementalErrorHandling', () => {
expect(ops).toEqual(['Foo']);
});
it('should not attempt to recover an unmounting error boundary', () => {
class Parent extends React.Component {
componentWillUnmount() {
ReactNoop.yield('Parent componentWillUnmount');
}
render() {
return <Boundary />;
}
}
class Boundary extends React.Component {
componentDidCatch(e) {
ReactNoop.yield(`Caught error: ${e.message}`);
}
render() {
return <ThrowsOnUnmount />;
}
}
class ThrowsOnUnmount extends React.Component {
componentWillUnmount() {
ReactNoop.yield('ThrowsOnUnmount componentWillUnmount');
throw new Error('unmount error');
}
render() {
return null;
}
}
ReactNoop.render(<Parent />);
ReactNoop.flush();
ReactNoop.render(null);
expect(ReactNoop.flush()).toEqual([
// Parent unmounts before the error is thrown.
'Parent componentWillUnmount',
'ThrowsOnUnmount componentWillUnmount',
]);
ReactNoop.render(<Parent />);
});
it('can unmount an error boundary before it is handled', () => {
let parent;
class Parent extends React.Component {
state = {step: 0};
render() {
parent = this;
return this.state.step === 0 ? <Boundary /> : null;
}
}
class Boundary extends React.Component {
componentDidCatch() {}
render() {
return <Child />;
}
}
class Child extends React.Component {
componentDidUpdate() {
parent.setState({step: 1});
throw new Error('update error');
}
render() {
return null;
}
}
ReactNoop.render(<Parent />);
ReactNoop.flush();
ReactNoop.flushSync(() => {
ReactNoop.render(<Parent />);
});
});
it('continues work on other roots despite caught errors', () => {
class ErrorBoundary extends React.Component {
state = {error: null};
@@ -896,7 +966,13 @@ describe('ReactIncrementalErrorHandling', () => {
}
try {
ReactNoop.render(<div><span><ErrorThrowingComponent /></span></div>);
ReactNoop.render(
<div>
<span>
<ErrorThrowingComponent />
</span>
</div>,
);
ReactNoop.flushDeferredPri();
} catch (error) {}
@@ -927,7 +1003,13 @@ describe('ReactIncrementalErrorHandling', () => {
}
try {
ReactNoop.render(<div><span><ErrorThrowingComponent /></span></div>);
ReactNoop.render(
<div>
<span>
<ErrorThrowingComponent />
</span>
</div>,
);
ReactNoop.flushDeferredPri();
} catch (error) {}
@@ -965,7 +1047,13 @@ describe('ReactIncrementalErrorHandling', () => {
);
try {
ReactNoop.render(<div><span><ErrorThrowingComponent /></span></div>);
ReactNoop.render(
<div>
<span>
<ErrorThrowingComponent />
</span>
</div>,
);
ReactNoop.flushDeferredPri();
} catch (error) {}

View File

@@ -1,17 +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 ReactTestRendererFeatureFlags
* @flow
*/
const ReactTestRendererFeatureFlags = {
enableTraversal: false,
};
module.exports = ReactTestRendererFeatureFlags;

View File

@@ -16,7 +16,6 @@
var ReactFiberReconciler = require('ReactFiberReconciler');
var ReactFiberTreeReflection = require('ReactFiberTreeReflection');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactTestRendererFeatureFlags = require('ReactTestRendererFeatureFlags');
var emptyObject = require('fbjs/lib/emptyObject');
var ReactTypeOfWork = require('ReactTypeOfWork');
var invariant = require('fbjs/lib/invariant');
@@ -568,11 +567,6 @@ var ReactTestRendererFiber = {
return {
get root() {
if (!ReactTestRendererFeatureFlags.enableTraversal) {
throw new Error(
'Test renderer traversal is experimental and not enabled',
);
}
if (root === null || root.current.child === null) {
throw new Error("Can't access .root on unmounted test renderer");
}

View File

@@ -20,8 +20,6 @@ const View = props => <RCTView {...props} />;
describe('ReactTestRendererTraversal', () => {
beforeEach(() => {
jest.resetModules();
const ReactTestRendererFeatureFlags = require('ReactTestRendererFeatureFlags');
ReactTestRendererFeatureFlags.enableTraversal = true;
ReactTestRenderer = require('react-test-renderer');
});