Files
react/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js
T

70 lines
2.1 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import invariant from 'shared/invariant';
const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
function unsupported() {
invariant(false, 'This feature is not supported by ReactSuspenseTestUtils.');
}
export function waitForSuspense<T>(fn: () => T): Promise<T> {
const cache: Map<Function, mixed> = new Map();
const testDispatcher: Dispatcher = {
getCacheForType<R>(resourceType: () => R): R {
let entry: R | void = (cache.get(resourceType): any);
if (entry === undefined) {
entry = resourceType();
// TODO: Warn if undefined?
cache.set(resourceType, entry);
}
return entry;
},
readContext: unsupported,
useContext: unsupported,
useMemo: unsupported,
useReducer: unsupported,
useRef: unsupported,
useState: unsupported,
useLayoutEffect: unsupported,
useCallback: unsupported,
useImperativeHandle: unsupported,
useEffect: unsupported,
useDebugValue: unsupported,
useDeferredValue: unsupported,
useTransition: unsupported,
useOpaqueIdentifier: unsupported,
useMutableSource: unsupported,
useCacheRefresh: unsupported,
};
// Not using async/await because we don't compile it.
return new Promise((resolve, reject) => {
function retry() {
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = testDispatcher;
try {
const result = fn();
resolve(result);
} catch (thrownValue) {
if (typeof thrownValue.then === 'function') {
thrownValue.then(retry, retry);
} else {
reject(thrownValue);
}
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
}
retry();
});
}