Skip to content

Commit

Permalink
Toggle visibility of Suspense children in mutation phase, not layout
Browse files Browse the repository at this point in the history
If parent reads visibility of children in a lifecycle, they should have
already updated.
  • Loading branch information
acdlite committed Nov 6, 2018
1 parent 8fab1de commit d046c96
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 24 deletions.
46 changes: 22 additions & 24 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,30 +449,8 @@ function commitLifeCycles(
}
return;
}
case SuspenseComponent: {
let newState: SuspenseState | null = finishedWork.memoizedState;

let newDidTimeout;
let primaryChildParent = finishedWork;
if (newState === null) {
newDidTimeout = false;
} else {
newDidTimeout = true;
primaryChildParent = finishedWork.child;
if (newState.timedOutAt === NoWork) {
// If the children had not already timed out, record the time.
// This is used to compute the elapsed time during subsequent
// attempts to render the children.
newState.timedOutAt = requestCurrentTime();
}
}

if (primaryChildParent !== null) {
hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
}

return;
}
case SuspenseComponent:
break;
case IncompleteClassComponent:
break;
default: {
Expand Down Expand Up @@ -1043,6 +1021,26 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
return;
}
case SuspenseComponent: {
let newState: SuspenseState | null = finishedWork.memoizedState;

let newDidTimeout;
let primaryChildParent = finishedWork;
if (newState === null) {
newDidTimeout = false;
} else {
newDidTimeout = true;
primaryChildParent = finishedWork.child;
if (newState.timedOutAt === NoWork) {
// If the children had not already timed out, record the time.
// This is used to compute the elapsed time during subsequent
// attempts to render the children.
newState.timedOutAt = requestCurrentTime();
}
}

if (primaryChildParent !== null) {
hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
}
return;
}
case IncompleteClassComponent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableHooks = true;
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
React = require('react');
Expand Down Expand Up @@ -1378,6 +1379,45 @@ describe('ReactSuspenseWithNoopRenderer', () => {
await advanceTimers(100);
expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
});

it('toggles visibility during the mutation phase', async () => {
const {useRef, useLayoutEffect} = React;

function Parent() {
const child = useRef(null);

useLayoutEffect(() => {
ReactNoop.yield('Child is hidden: ' + child.current.hidden);
});

return (
<span ref={child} hidden={false}>
<AsyncText ms={1000} text="Hi" />
</span>
);
}

function App(props) {
return (
<Suspense fallback={<Text text="Loading..." />}>
<Parent />
</Suspense>
);
}

ReactNoop.renderLegacySyncRoot(<App middleText="B" />);

expect(ReactNoop.clearYields()).toEqual([
'Suspend! [Hi]',
'Loading...',
// The child should have already been hidden
'Child is hidden: true',
]);

await advanceTimers(1000);

expect(ReactNoop.clearYields()).toEqual(['Promise resolved [Hi]', 'Hi']);
});
});

it('does not call lifecycles of a suspended component', async () => {
Expand Down

0 comments on commit d046c96

Please sign in to comment.