@samitouri / QOS-React / commits / b245795de3

Re-enable Flow for ReactFiber and fix Flow issues (#12842)

* Lint for untyped imports and enable Flow typing in ReactFiber * Re-enable Flow for ReactFiber and fix Flow issues * Avoid an invariant in DEV-only code I just introduced it, but on a second thought, it's better to keep it as a warning. * Address review

Dan Abramov committed May 17, 2018 at 17:14 UTC b245795de373ca8f4a6c4962b59773e2f1bb2f1d
7 files changed +120 -108
.flowconfig
+3
@@ -21,6 +21,9 @@
21 ./node_modules/fbjs/flow/lib/dev.js
22 ./scripts/flow
23
24 +[lints]
25 +untyped-type-import=error
26 +
27 [options]
28 esproposal.class_static_fields=enable
29 esproposal.class_instance_fields=enable
packages/react-dom/src/events/ReactDOMEventListener.js
+1 -1
@@ -54,7 +54,7 @@ function getTopLevelCallbackBookKeeping(
54 ): {
55 topLevelType: ?DOMTopLevelEventType,
56 nativeEvent: ?AnyNativeEvent,
57 - targetInst: Fiber,
57 + targetInst: Fiber | null,
58 ancestors: Array<Fiber>,
59 } {
60 if (callbackBookkeepingPool.length) {
packages/react-reconciler/src/ReactCapturedValue.js
+1 -1
@@ -29,7 +29,7 @@ export type CapturedError = {
29
30 export function createCapturedValue<T>(
31 value: T,
32 - source: Fiber | null,
32 + source: Fiber,
33 ): CapturedValue<T> {
34 // If the value is an error, call this function immediately after it is thrown
35 // so the stack is accurate.
packages/react-reconciler/src/ReactFiber.js
+45 -63
@@ -4,10 +4,11 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 + * @flow
8 */
9
10 import type {ReactElement, Source} from 'shared/ReactElementType';
10 -import type {ReactPortal, RefObject} from 'shared/ReactTypes';
11 +import type {ReactFragment, ReactPortal, RefObject} from 'shared/ReactTypes';
12 import type {TypeOfWork} from 'shared/ReactTypeOfWork';
13 import type {TypeOfMode} from './ReactTypeOfMode';
14 import type {TypeOfSideEffect} from 'shared/ReactTypeOfSideEffect';
@@ -316,7 +317,7 @@ export function createWorkInProgress(
317 return workInProgress;
318 }
319
319 -export function createHostRootFiber(isAsync): Fiber {
320 +export function createHostRootFiber(isAsync: boolean): Fiber {
321 const mode = isAsync ? AsyncMode | StrictMode : NoContext;
322 return createFiber(HostRoot, null, null, mode);
323 }
@@ -366,42 +367,9 @@ export function createFiberFromElement(
367 // mode compatible.
368 mode |= StrictMode;
369 break;
369 - default: {
370 - if (typeof type === 'object' && type !== null) {
371 - switch (type.$$typeof) {
372 - case REACT_PROVIDER_TYPE:
373 - fiberTag = ContextProvider;
374 - break;
375 - case REACT_CONTEXT_TYPE:
376 - // This is a consumer
377 - fiberTag = ContextConsumer;
378 - break;
379 - case REACT_FORWARD_REF_TYPE:
380 - fiberTag = ForwardRef;
381 - break;
382 - default:
383 - if (typeof type.tag === 'number') {
384 - // Currently assumed to be a continuation and therefore is a
385 - // fiber already.
386 - // TODO: The yield system is currently broken for updates in
387 - // some cases. The reified yield stores a fiber, but we don't
388 - // know which fiber that is; the current or a workInProgress?
389 - // When the continuation gets rendered here we don't know if we
390 - // can reuse that fiber or if we need to clone it. There is
391 - // probably a clever way to restructure this.
392 - fiber = ((type: any): Fiber);
393 - fiber.pendingProps = pendingProps;
394 - fiber.expirationTime = expirationTime;
395 - return fiber;
396 - } else {
397 - throwOnInvalidElementType(type, owner);
398 - }
399 - break;
400 - }
401 - } else {
402 - throwOnInvalidElementType(type, owner);
403 - }
404 - }
370 + default:
371 + fiberTag = getFiberTagFromObjectType(type, owner);
372 + break;
373 }
374 }
375
@@ -417,33 +385,47 @@ export function createFiberFromElement(
385 return fiber;
386 }
387
420 -function throwOnInvalidElementType(type, owner) {
421 - let info = '';
422 - if (__DEV__) {
423 - if (
424 - type === undefined ||
425 - (typeof type === 'object' &&
426 - type !== null &&
427 - Object.keys(type).length === 0)
428 - ) {
429 - info +=
430 - ' You likely forgot to export your component from the file ' +
431 - "it's defined in, or you might have mixed up default and " +
432 - 'named imports.';
433 - }
434 - const ownerName = owner ? getComponentName(owner) : null;
435 - if (ownerName) {
436 - info += '\n\nCheck the render method of `' + ownerName + '`.';
388 +function getFiberTagFromObjectType(type, owner): TypeOfWork {
389 + const $$typeof =
390 + typeof type === 'object' && type !== null ? type.$$typeof : null;
391 +
392 + switch ($$typeof) {
393 + case REACT_PROVIDER_TYPE:
394 + return ContextProvider;
395 + case REACT_CONTEXT_TYPE:
396 + // This is a consumer
397 + return ContextConsumer;
398 + case REACT_FORWARD_REF_TYPE:
399 + return ForwardRef;
400 + default: {
401 + let info = '';
402 + if (__DEV__) {
403 + if (
404 + type === undefined ||
405 + (typeof type === 'object' &&
406 + type !== null &&
407 + Object.keys(type).length === 0)
408 + ) {
409 + info +=
410 + ' You likely forgot to export your component from the file ' +
411 + "it's defined in, or you might have mixed up default and " +
412 + 'named imports.';
413 + }
414 + const ownerName = owner ? getComponentName(owner) : null;
415 + if (ownerName) {
416 + info += '\n\nCheck the render method of `' + ownerName + '`.';
417 + }
418 + }
419 + invariant(
420 + false,
421 + 'Element type is invalid: expected a string (for built-in ' +
422 + 'components) or a class/function (for composite components) ' +
423 + 'but got: %s.%s',
424 + type == null ? type : typeof type,
425 + info,
426 + );
427 }
428 }
439 - invariant(
440 - false,
441 - 'Element type is invalid: expected a string (for built-in ' +
442 - 'components) or a class/function (for composite components) ' +
443 - 'but got: %s.%s',
444 - type == null ? type : typeof type,
445 - info,
446 - );
429 }
430
431 export function createFiberFromFragment(
packages/react-reconciler/src/ReactFiberCommitWork.js
+3 -3
@@ -9,7 +9,7 @@
9
10 import type {HostConfig} from 'react-reconciler';
11 import type {Fiber} from './ReactFiber';
12 -import type {FiberRoot} from './ReactFiber';
12 +import type {FiberRoot} from './ReactFiberRoot';
13 import type {ExpirationTime} from './ReactFiberExpirationTime';
14 import type {CapturedValue, CapturedError} from './ReactCapturedValue';
15
@@ -59,7 +59,7 @@ if (__DEV__) {
59 export function logError(boundary: Fiber, errorInfo: CapturedValue<mixed>) {
60 const source = errorInfo.source;
61 let stack = errorInfo.stack;
62 - if (stack === null) {
62 + if (stack === null && source !== null) {
63 stack = getStackAddendumByWorkInProgressFiber(source);
64 }
65
@@ -94,7 +94,7 @@ export function logError(boundary: Fiber, errorInfo: CapturedValue<mixed>) {
94
95 export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
96 config: HostConfig<T, P, I, TI, HI, PI, C, CC, CX, PL>,
97 - captureError: (failedFiber: Fiber, error: mixed) => Fiber | null,
97 + captureError: (failedFiber: Fiber, error: mixed) => void,
98 scheduleWork: (
99 fiber: Fiber,
100 startTime: ExpirationTime,
packages/react-reconciler/src/ReactFiberScheduler.js
+52 -33
@@ -321,10 +321,20 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
321 }
322
323 // Restore the original state of the work-in-progress
324 + if (stashedWorkInProgressProperties === null) {
325 + // This should never happen. Don't throw because this code is DEV-only.
326 + warning(
327 + false,
328 + 'Could not replay rendering after an error. This is likely a bug in React. ' +
329 + 'Please file an issue.',
330 + );
331 + return;
332 + }
333 assignFiberPropertiesInDEV(
334 failedUnitOfWork,
335 stashedWorkInProgressProperties,
336 );
337 +
338 switch (failedUnitOfWork.tag) {
339 case HostRoot:
340 popHostContainer(failedUnitOfWork);
@@ -1073,43 +1083,52 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
1083 // This is a fatal error.
1084 didFatal = true;
1085 onUncaughtError(thrownValue);
1076 - break;
1077 - }
1086 + } else {
1087 + if (__DEV__) {
1088 + // Reset global debug state
1089 + // We assume this is defined in DEV
1090 + (resetCurrentlyProcessingQueue: any)();
1091 + }
1092
1079 - if (__DEV__) {
1080 - // Reset global debug state
1081 - // We assume this is defined in DEV
1082 - (resetCurrentlyProcessingQueue: any)();
1083 - }
1093 + const failedUnitOfWork: Fiber = nextUnitOfWork;
1094 + if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
1095 + replayUnitOfWork(failedUnitOfWork, thrownValue, isAsync);
1096 + }
1097
1085 - if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
1086 - const failedUnitOfWork = nextUnitOfWork;
1087 - replayUnitOfWork(failedUnitOfWork, thrownValue, isAsync);
1088 - }
1098 + // TODO: we already know this isn't true in some cases.
1099 + // At least this shows a nicer error message until we figure out the cause.
1100 + // https://github.com/facebook/react/issues/12449#issuecomment-386727431
1101 + invariant(
1102 + nextUnitOfWork !== null,
1103 + 'Failed to replay rendering after an error. This ' +
1104 + 'is likely caused by a bug in React. Please file an issue ' +
1105 + 'with a reproducing case to help us find it.',
1106 + );
1107
1090 - const sourceFiber: Fiber = nextUnitOfWork;
1091 - let returnFiber = sourceFiber.return;
1092 - if (returnFiber === null) {
1093 - // This is the root. The root could capture its own errors. However,
1094 - // we don't know if it errors before or after we pushed the host
1095 - // context. This information is needed to avoid a stack mismatch.
1096 - // Because we're not sure, treat this as a fatal error. We could track
1097 - // which phase it fails in, but doesn't seem worth it. At least
1098 - // for now.
1099 - didFatal = true;
1100 - onUncaughtError(thrownValue);
1101 - break;
1108 + const sourceFiber: Fiber = nextUnitOfWork;
1109 + let returnFiber = sourceFiber.return;
1110 + if (returnFiber === null) {
1111 + // This is the root. The root could capture its own errors. However,
1112 + // we don't know if it errors before or after we pushed the host
1113 + // context. This information is needed to avoid a stack mismatch.
1114 + // Because we're not sure, treat this as a fatal error. We could track
1115 + // which phase it fails in, but doesn't seem worth it. At least
1116 + // for now.
1117 + didFatal = true;
1118 + onUncaughtError(thrownValue);
1119 + break;
1120 + }
1121 + throwException(
1122 + root,
1123 + returnFiber,
1124 + sourceFiber,
1125 + thrownValue,
1126 + nextRenderIsExpired,
1127 + nextRenderExpirationTime,
1128 + mostRecentCurrentTimeMs,
1129 + );
1130 + nextUnitOfWork = completeUnitOfWork(sourceFiber);
1131 }
1103 - throwException(
1104 - root,
1105 - returnFiber,
1106 - sourceFiber,
1107 - thrownValue,
1108 - nextRenderIsExpired,
1109 - nextRenderExpirationTime,
1110 - mostRecentCurrentTimeMs,
1111 - );
1112 - nextUnitOfWork = completeUnitOfWork(sourceFiber);
1132 }
1133 break;
1134 } while (true);
packages/react-reconciler/src/ReactStrictModeWarnings.js
+15 -7
@@ -107,15 +107,15 @@ if (__DEV__) {
107 pendingUnsafeLifecycleWarnings = new Map();
108 };
109
110 - const getStrictRoot = (fiber: Fiber): Fiber => {
110 + const findStrictRoot = (fiber: Fiber): Fiber | null => {
111 let maybeStrictRoot = null;
112
113 - while (fiber !== null) {
114 - if (fiber.mode & StrictMode) {
115 - maybeStrictRoot = fiber;
113 + let node = fiber;
114 + while (node !== null) {
115 + if (node.mode & StrictMode) {
116 + maybeStrictRoot = node;
117 }
117 -
118 - fiber = fiber.return;
118 + node = node.return;
119 }
120
121 return maybeStrictRoot;
@@ -225,7 +225,15 @@ if (__DEV__) {
225 fiber: Fiber,
226 instance: any,
227 ) => {
228 - const strictRoot = getStrictRoot(fiber);
228 + const strictRoot = findStrictRoot(fiber);
229 + if (strictRoot === null) {
230 + warning(
231 + false,
232 + 'Expected to find a StrictMode component in a strict mode tree. ' +
233 + 'This error is likely caused by a bug in React. Please file an issue.',
234 + );
235 + return;
236 + }
237
238 // Dedup strategy: Warn once per component.
239 // This is difficult to track any other way since component names