Skip to content

Commit

Permalink
Make React.Suspense "fallback" prop optional (and add nested Suspense…
Browse files Browse the repository at this point in the history
… test cases)
  • Loading branch information
Brian Vaughn committed Nov 28, 2018
1 parent 2fdcda5 commit dabf49e
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 38 deletions.
2 changes: 1 addition & 1 deletion lib/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ declare module react {

declare export var Suspense: React$ComponentType<{
children?: ?React$Node,
fallback: React$Node,
fallback?: React$Node,
maxDuration?: number
}>; // 16.6+

Expand Down
26 changes: 17 additions & 9 deletions tests/react_16_6/Suspense.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ function Loading() {
return <div>Loading...</div>;
}

{
<Suspense /> // Error: fallback is missing in props
}

{
<Suspense fallback={<Loading />} maxDuration="abc" /> // Error: string is incompatible with number
}
Expand All @@ -24,18 +20,30 @@ function Loading() {
<Suspense fallback={<Loading/>} />
}

{
<Suspense fallback={<Loading/>} maxDuration={1000} />
}

{
<Suspense fallback={<Loading/>} maxDuration={1000}>
<div>Hello</div>
</Suspense>
}

{
<Suspense fallback={<Loading/>}>
<Loading />
<Suspense />
</Suspense>
}

{
<Suspense fallback={<Loading/>} maxDuration={1000} />
<Suspense fallback={<Loading/>}>
<Suspense fallback={undefined} />
</Suspense>
}

{
<Suspense fallback={<Loading/>} maxDuration={1000}>
<Loading />
<Suspense fallback={<Loading/>}>
<Suspense fallback={null} />
</Suspense>
}
}
35 changes: 8 additions & 27 deletions tests/react_16_6/react_16_6.exp
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
Error ------------------------------------------------------------------------------------------------- Suspense.js:12:3

Cannot create `Suspense` element because property `fallback` is missing in props [1] but exists in object type [2].

Suspense.js:12:3
12| <Suspense /> // Error: fallback is missing in props
^^^^^^^^^^^^ [1]

References:
<BUILTINS>/react.js:253:52
v
253| declare export var Suspense: React$ComponentType<{
254| children?: ?React$Node,
255| fallback: React$Node,
256| maxDuration?: number
257| }>; // 16.6+
^ [2]


Error ------------------------------------------------------------------------------------------------- Suspense.js:16:3

Cannot create `Suspense` element because string [1] is incompatible with number [2] in property `maxDuration`.

Suspense.js:16:3
16| <Suspense fallback={<Loading />} maxDuration="abc" /> // Error: string is incompatible with number
Suspense.js:12:3
12| <Suspense fallback={<Loading />} maxDuration="abc" /> // Error: string is incompatible with number
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

References:
Suspense.js:16:48
16| <Suspense fallback={<Loading />} maxDuration="abc" /> // Error: string is incompatible with number
Suspense.js:12:48
12| <Suspense fallback={<Loading />} maxDuration="abc" /> // Error: string is incompatible with number
^^^^^ [1]
<BUILTINS>/react.js:256:19
256| maxDuration?: number
^^^^^^ [2]


Error ------------------------------------------------------------------------------------------------- Suspense.js:20:3
Error ------------------------------------------------------------------------------------------------- Suspense.js:16:3

Cannot create `Suspense` element because in property `fallback`:
- Either inexact function [1] is incompatible with exact `React.Element` [2].
- Or function [1] is incompatible with `React.Portal` [3].
- Or property `@@iterator` is missing in function [1] but exists in `$Iterable` [4].

Suspense.js:20:3
20| <Suspense fallback={Loading} /> // Error: function is incompatible with exact React.Element
Suspense.js:16:3
16| <Suspense fallback={Loading} /> // Error: function is incompatible with exact React.Element
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

References:
Expand Down Expand Up @@ -241,7 +222,7 @@ References:



Found 15 errors
Found 14 errors

Only showing the most relevant union/intersection branches.
To see all branches, re-run Flow with --show-all-branches
2 changes: 1 addition & 1 deletion tests/type-at-pos_react/type-at-pos_react.exp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
react_component.js:3:9 = {
"type":"{|+Children: {+count: (children: ChildrenArray<any>) => number, +forEach: <T>(children: ChildrenArray<T>, fn: (child: T, index: number) => mixed, thisArg?: mixed) => void, +map: <T, U>(children: ChildrenArray<T>, fn: (child: $NonMaybeType<T>, index: number) => U, thisArg?: mixed) => Array<$NonMaybeType<U>>, +only: <T>(children: ChildrenArray<T>) => $NonMaybeType<T>, +toArray: <T>(children: ChildrenArray<T>) => Array<$NonMaybeType<T>>}, +ChildrenArray: type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T, +Component: class React$Component<Props, State = void>, +ComponentType: type ComponentType<P> = React$ComponentType<P>, +ConcurrentMode: ({children: ?React$Node}) => React$Node, +Context: type Context<T> = React$Context<T>, +DOM: any, +Element: type Element<+C> = React$Element<C>, +ElementConfig: type ElementConfig<C> = React$ElementConfig<C>, +ElementProps: type ElementProps<C> = React$ElementProps<C>, +ElementRef: type ElementRef<C> = React$ElementRef<C>, +ElementType: type ElementType = React$ElementType, +Fragment: ({children: ?React$Node}) => React$Node, +Key: type Key = React$Key, +Node: type Node = React$Node, +Portal: type Portal = React$Portal, +PropTypes: ReactPropTypes, +PureComponent: class React$PureComponent<Props, State = void>, +Ref: type Ref<C> = React$Ref<C>, +StatelessFunctionalComponent: type StatelessFunctionalComponent<P> = React$StatelessFunctionalComponent<P>, +StrictMode: ({children: ?React$Node}) => React$Node, +Suspense: React$ComponentType<{children?: ?React$Node, fallback: React$Node, maxDuration?: number}>, +checkPropTypes: <V>(propTypes: $Subtype<{[_: $Keys<V>]: ReactPropsCheckType}>, values: V, location: string, componentName: string, getStack: ?(() => ?string)) => void, +cloneElement: React$CloneElement, +createClass: React$CreateClass, +createContext: <T>(defaultValue: T, calculateChangedBits: ?((a: T, b: T) => number)) => React$Context<T>, +createElement: React$CreateElement, +createFactory: <ElementType: React$ElementType>(type: ElementType) => React$ElementFactory<ElementType>, +createRef: <T>() => {current: (null | T)}, +default: {|+Children: {+count: (children: ChildrenArray<any>) => number, +forEach: <T>(children: ChildrenArray<T>, fn: (child: T, index: number) => mixed, thisArg?: mixed) => void, +map: <T, U>(children: ChildrenArray<T>, fn: (child: $NonMaybeType<T>, index: number) => U, thisArg?: mixed) => Array<$NonMaybeType<U>>, +only: <T>(children: ChildrenArray<T>) => $NonMaybeType<T>, +toArray: <T>(children: ChildrenArray<T>) => Array<$NonMaybeType<T>>}, +Component: class React$Component<Props, State = void>, +ConcurrentMode: ({children: ?React$Node}) => React$Node, +DOM: any, +Fragment: ({children: ?React$Node}) => React$Node, +PropTypes: ReactPropTypes, +PureComponent: class React$PureComponent<Props, State = void>, +StrictMode: ({children: ?React$Node}) => React$Node, +Suspense: React$ComponentType<{children?: ?React$Node, fallback: React$Node, maxDuration?: number}>, +checkPropTypes: <V>(propTypes: $Subtype<{[_: $Keys<V>]: ReactPropsCheckType}>, values: V, location: string, componentName: string, getStack: ?(() => ?string)) => void, +cloneElement: React$CloneElement, +createClass: React$CreateClass, +createContext: <T>(defaultValue: T, calculateChangedBits: ?((a: T, b: T) => number)) => React$Context<T>, +createElement: React$CreateElement, +createFactory: <ElementType: React$ElementType>(type: ElementType) => React$ElementFactory<ElementType>, +createRef: <T>() => {current: (null | T)}, +isValidElement: (element: any) => boolean, +lazy: <P>(component: () => React$ComponentType<P>) => React$ComponentType<P>, +memo: <P>(component: React$StatelessFunctionalComponent<P>, equal?: (P, P) => boolean) => React$StatelessFunctionalComponent<P>, +useCallback: <T>(callback: () => (T | void), inputs: ?$ReadOnlyArray<mixed>) => T, +useContext: <T>(context: React$Context<T>, observedBits: (void | number | boolean)) => T, +useEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useImperativeMethods: <T>(ref: ?({current: (T | null)} | ((inst: (T | null)) => mixed)), create: () => T, inputs: ?$ReadOnlyArray<mixed>) => void, +useLayoutEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useMemo: <T>(create: () => T, inputs: ?$ReadOnlyArray<mixed>) => T, +useReducer: <S, A>(reducer: (S, A) => S, initialState: S, initialAction: ?A) => [S, (A) => void], +useRef: <T>(initialValue: ?T) => {current: (T | null)}, +useState: <S>(initialState: ((() => S) | S)) => [S, ((((S) => S) | S)) => void], +version: string|}, +isValidElement: (element: any) => boolean, +lazy: <P>(component: () => React$ComponentType<P>) => React$ComponentType<P>, +memo: <P>(component: React$StatelessFunctionalComponent<P>, equal?: (P, P) => boolean) => React$StatelessFunctionalComponent<P>, +useCallback: <T>(callback: () => (T | void), inputs: ?$ReadOnlyArray<mixed>) => T, +useContext: <T>(context: React$Context<T>, observedBits: (void | number | boolean)) => T, +useEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useImperativeMethods: <T>(ref: ?({current: (T | null)} | ((inst: (T | null)) => mixed)), create: () => T, inputs: ?$ReadOnlyArray<mixed>) => void, +useLayoutEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useMemo: <T>(create: () => T, inputs: ?$ReadOnlyArray<mixed>) => T, +useReducer: <S, A>(reducer: (S, A) => S, initialState: S, initialAction: ?A) => [S, (A) => void], +useRef: <T>(initialValue: ?T) => {current: (T | null)}, +useState: <S>(initialState: ((() => S) | S)) => [S, ((((S) => S) | S)) => void], +version: string|}",
"type":"{|+Children: {+count: (children: ChildrenArray<any>) => number, +forEach: <T>(children: ChildrenArray<T>, fn: (child: T, index: number) => mixed, thisArg?: mixed) => void, +map: <T, U>(children: ChildrenArray<T>, fn: (child: $NonMaybeType<T>, index: number) => U, thisArg?: mixed) => Array<$NonMaybeType<U>>, +only: <T>(children: ChildrenArray<T>) => $NonMaybeType<T>, +toArray: <T>(children: ChildrenArray<T>) => Array<$NonMaybeType<T>>}, +ChildrenArray: type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T, +Component: class React$Component<Props, State = void>, +ComponentType: type ComponentType<P> = React$ComponentType<P>, +ConcurrentMode: ({children: ?React$Node}) => React$Node, +Context: type Context<T> = React$Context<T>, +DOM: any, +Element: type Element<+C> = React$Element<C>, +ElementConfig: type ElementConfig<C> = React$ElementConfig<C>, +ElementProps: type ElementProps<C> = React$ElementProps<C>, +ElementRef: type ElementRef<C> = React$ElementRef<C>, +ElementType: type ElementType = React$ElementType, +Fragment: ({children: ?React$Node}) => React$Node, +Key: type Key = React$Key, +Node: type Node = React$Node, +Portal: type Portal = React$Portal, +PropTypes: ReactPropTypes, +PureComponent: class React$PureComponent<Props, State = void>, +Ref: type Ref<C> = React$Ref<C>, +StatelessFunctionalComponent: type StatelessFunctionalComponent<P> = React$StatelessFunctionalComponent<P>, +StrictMode: ({children: ?React$Node}) => React$Node, +Suspense: React$ComponentType<{children?: ?React$Node, fallback?: React$Node, maxDuration?: number}>, +checkPropTypes: <V>(propTypes: $Subtype<{[_: $Keys<V>]: ReactPropsCheckType}>, values: V, location: string, componentName: string, getStack: ?(() => ?string)) => void, +cloneElement: React$CloneElement, +createClass: React$CreateClass, +createContext: <T>(defaultValue: T, calculateChangedBits: ?((a: T, b: T) => number)) => React$Context<T>, +createElement: React$CreateElement, +createFactory: <ElementType: React$ElementType>(type: ElementType) => React$ElementFactory<ElementType>, +createRef: <T>() => {current: (null | T)}, +default: {|+Children: {+count: (children: ChildrenArray<any>) => number, +forEach: <T>(children: ChildrenArray<T>, fn: (child: T, index: number) => mixed, thisArg?: mixed) => void, +map: <T, U>(children: ChildrenArray<T>, fn: (child: $NonMaybeType<T>, index: number) => U, thisArg?: mixed) => Array<$NonMaybeType<U>>, +only: <T>(children: ChildrenArray<T>) => $NonMaybeType<T>, +toArray: <T>(children: ChildrenArray<T>) => Array<$NonMaybeType<T>>}, +Component: class React$Component<Props, State = void>, +ConcurrentMode: ({children: ?React$Node}) => React$Node, +DOM: any, +Fragment: ({children: ?React$Node}) => React$Node, +PropTypes: ReactPropTypes, +PureComponent: class React$PureComponent<Props, State = void>, +StrictMode: ({children: ?React$Node}) => React$Node, +Suspense: React$ComponentType<{children?: ?React$Node, fallback?: React$Node, maxDuration?: number}>, +checkPropTypes: <V>(propTypes: $Subtype<{[_: $Keys<V>]: ReactPropsCheckType}>, values: V, location: string, componentName: string, getStack: ?(() => ?string)) => void, +cloneElement: React$CloneElement, +createClass: React$CreateClass, +createContext: <T>(defaultValue: T, calculateChangedBits: ?((a: T, b: T) => number)) => React$Context<T>, +createElement: React$CreateElement, +createFactory: <ElementType: React$ElementType>(type: ElementType) => React$ElementFactory<ElementType>, +createRef: <T>() => {current: (null | T)}, +isValidElement: (element: any) => boolean, +lazy: <P>(component: () => React$ComponentType<P>) => React$ComponentType<P>, +memo: <P>(component: React$StatelessFunctionalComponent<P>, equal?: (P, P) => boolean) => React$StatelessFunctionalComponent<P>, +useCallback: <T>(callback: () => (T | void), inputs: ?$ReadOnlyArray<mixed>) => T, +useContext: <T>(context: React$Context<T>, observedBits: (void | number | boolean)) => T, +useEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useImperativeMethods: <T>(ref: ?({current: (T | null)} | ((inst: (T | null)) => mixed)), create: () => T, inputs: ?$ReadOnlyArray<mixed>) => void, +useLayoutEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useMemo: <T>(create: () => T, inputs: ?$ReadOnlyArray<mixed>) => T, +useReducer: <S, A>(reducer: (S, A) => S, initialState: S, initialAction: ?A) => [S, (A) => void], +useRef: <T>(initialValue: ?T) => {current: (T | null)}, +useState: <S>(initialState: ((() => S) | S)) => [S, ((((S) => S) | S)) => void], +version: string|}, +isValidElement: (element: any) => boolean, +lazy: <P>(component: () => React$ComponentType<P>) => React$ComponentType<P>, +memo: <P>(component: React$StatelessFunctionalComponent<P>, equal?: (P, P) => boolean) => React$StatelessFunctionalComponent<P>, +useCallback: <T>(callback: () => (T | void), inputs: ?$ReadOnlyArray<mixed>) => T, +useContext: <T>(context: React$Context<T>, observedBits: (void | number | boolean)) => T, +useEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useImperativeMethods: <T>(ref: ?({current: (T | null)} | ((inst: (T | null)) => mixed)), create: () => T, inputs: ?$ReadOnlyArray<mixed>) => void, +useLayoutEffect: (create: () => MaybeCleanUpFn, inputs: ?$ReadOnlyArray<mixed>) => void, +useMemo: <T>(create: () => T, inputs: ?$ReadOnlyArray<mixed>) => T, +useReducer: <S, A>(reducer: (S, A) => S, initialState: S, initialAction: ?A) => [S, (A) => void], +useRef: <T>(initialValue: ?T) => {current: (T | null)}, +useState: <S>(initialState: ((() => S) | S)) => [S, ((((S) => S) | S)) => void], +version: string|}",
"reasons":[],
"loc":{
"source":"react_component.js",
Expand Down

0 comments on commit dabf49e

Please sign in to comment.