Skip to content

Commit

Permalink
Allow DevTools to toggle Suspense state
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Mar 29, 2019
1 parent 80f8b0d commit da14cfb
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 1 deletion.
Expand Up @@ -16,15 +16,25 @@ describe('React hooks DevTools integration', () => {
let ReactTestRenderer;
let act;
let overrideHookState;
let overrideProps;
let suspendedFibers;

beforeEach(() => {
suspendedFibers = new Set();
global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
inject: injected => {
overrideHookState = injected.overrideHookState;
overrideProps = injected.overrideProps;
},
supportsFiber: true,
onCommitFiberRoot: () => {},
onCommitFiberUnmount: () => {},
shouldSuspendFiber(rendererId, fiber) {
return (
suspendedFibers.has(fiber) ||
(fiber.alternate && suspendedFibers.has(fiber.alternate))
);
},
};

jest.resetModules();
Expand Down Expand Up @@ -173,4 +183,43 @@ describe('React hooks DevTools integration', () => {
});
}
});

it('should support triggering suspense in DEV', () => {
let setCountFn;

function MyComponent() {
return 'Done';
}

const renderer = ReactTestRenderer.create(
<React.Suspense fallback={'Loading'}>
<MyComponent />
</React.Suspense>,
);
expect(renderer.toJSON()).toEqual('Done');

const fiber = renderer.root._currentFiber().return;
if (__DEV__) {
// Mark as loading
suspendedFibers.add(fiber);
overrideProps(fiber, [], null); // Re-render
expect(renderer.toJSON()).toEqual('Loading');

overrideProps(fiber, [], null); // Re-render
expect(renderer.toJSON()).toEqual('Loading');

// Mark as done
suspendedFibers.delete(fiber);
overrideProps(fiber, [], null); // Re-render
expect(renderer.toJSON()).toEqual('Done');

overrideProps(fiber, [], null); // Re-render
expect(renderer.toJSON()).toEqual('Done');

// Mark as loading again
suspendedFibers.add(fiber);
overrideProps(fiber, [], null); // Re-render
expect(renderer.toJSON()).toEqual('Loading');
}
});
});
7 changes: 7 additions & 0 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Expand Up @@ -77,6 +77,7 @@ import {
cloneChildFibers,
} from './ReactChildFiber';
import {processUpdateQueue} from './ReactUpdateQueue';
import {shouldSuspend} from './ReactFiberDevToolsHook';
import {
NoWork,
Never,
Expand Down Expand Up @@ -1389,6 +1390,12 @@ function updateSuspenseComponent(
const mode = workInProgress.mode;
const nextProps = workInProgress.pendingProps;

if (__DEV__) {
if (shouldSuspend(workInProgress)) {
workInProgress.effectTag |= DidCapture;
}
}

// We should attempt to render the primary children unless this boundary
// already suspended during this render (`alreadyCaptured` is true).
let nextState: SuspenseState | null = workInProgress.memoizedState;
Expand Down
14 changes: 14 additions & 0 deletions packages/react-reconciler/src/ReactFiberDevToolsHook.js
Expand Up @@ -16,6 +16,9 @@ declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;

let onCommitFiberRoot = null;
let onCommitFiberUnmount = null;
let shouldSuspendFiber = function() {
return false;
};
let hasLoggedError = false;

function catchErrors(fn) {
Expand Down Expand Up @@ -71,6 +74,13 @@ export function injectInternals(internals: Object): boolean {
onCommitFiberUnmount = catchErrors(fiber =>
hook.onCommitFiberUnmount(rendererID, fiber),
);
if (__DEV__) {
if (hook.shouldSuspendFiber) {
shouldSuspendFiber = catchErrors(fiber =>
hook.shouldSuspendFiber(rendererID, fiber),
);
}
}
} catch (err) {
// Catch all errors because it is unsafe to throw during initialization.
if (__DEV__) {
Expand All @@ -96,3 +106,7 @@ export function onCommitUnmount(fiber: Fiber) {
onCommitFiberUnmount(fiber);
}
}

export function shouldSuspend(fiber: Fiber) {
return shouldSuspendFiber(fiber);
}
6 changes: 5 additions & 1 deletion packages/react-reconciler/src/ReactFiberReconciler.js
Expand Up @@ -404,7 +404,11 @@ if (__DEV__) {
// Support DevTools props for function components, forwardRef, memo, host components, etc.
overrideProps = (fiber: Fiber, path: Array<string | number>, value: any) => {
flushPassiveEffects();
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
if (path.length > 0) {
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
} else {
fiber.pendingProps = {...fiber.pendingProps};
}
if (fiber.alternate) {
fiber.alternate.pendingProps = fiber.pendingProps;
}
Expand Down
Expand Up @@ -103,6 +103,7 @@ const mockDevToolsForTest = () => {
onCommitRoot: () => {},
onCommitUnmount: () => {},
isDevToolsPresent: true,
shouldSuspend: () => false,
}));
};

Expand Down

0 comments on commit da14cfb

Please sign in to comment.