Skip to content

Commit

Permalink
Fix a crash in Suspense with findDOMNode
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon authored and acdlite committed Apr 3, 2019
1 parent 6d0effa commit 41aa345
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,48 @@ describe('ReactDOMSuspensePlaceholder', () => {
await Lazy;
expect(log).toEqual(['cDU first', 'cDU second']);
});

// Regression test for https://github.com/facebook/react/issues/14188
it('can call findDOMNode() in a suspended component commit phase (#2)', () => {
let suspendOnce = Promise.resolve();
function Suspend() {
if (suspendOnce) {
let promise = suspendOnce;
suspendOnce = null;
throw promise;
}
return null;
}

const log = [];
class Child extends React.Component {
componentDidMount() {
log.push('cDM');
ReactDOM.findDOMNode(this);
}

componentDidUpdate() {
log.push('cDU');
ReactDOM.findDOMNode(this);
}

render() {
return null;
}
}

function App() {
return (
<Suspense fallback="Loading">
<Suspend />
<Child />
</Suspense>
);
}

ReactDOM.render(<App />, container);
expect(log).toEqual(['cDM']);
ReactDOM.render(<App />, container);
expect(log).toEqual(['cDM', 'cDU']);
});
});
25 changes: 23 additions & 2 deletions packages/react-reconciler/src/ReactFiberTreeReflection.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
HostRoot,
HostPortal,
HostText,
Fragment,
SuspenseComponent,
} from 'shared/ReactWorkTags';
import {NoEffect, Placement} from 'shared/ReactSideEffectTags';

Expand Down Expand Up @@ -119,8 +121,27 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
let parentA = a.return;
let parentB = parentA ? parentA.alternate : null;
if (!parentA || !parentB) {
// We're at the root.
break;
// We're either at the root, or we're in a special Fragment
// with no alternate, which is how Suspense (un)hiding works.
let maybeSuspenseFragment = parentA || parentB;
if (maybeSuspenseFragment && maybeSuspenseFragment.tag === Fragment) {
const maybeSuspense = maybeSuspenseFragment.return;
if (
maybeSuspense &&
maybeSuspense.tag === SuspenseComponent &&
// If state isn't null, it timed out and we have two Fragment children.
maybeSuspense.memoizedState !== null
) {
parentA = maybeSuspense;
parentB = maybeSuspense;
a = maybeSuspenseFragment;
b = maybeSuspenseFragment;
} else {
break;
}
} else {
break;
}
}

// If both copies of the parent fiber point to the same child, we can
Expand Down

0 comments on commit 41aa345

Please sign in to comment.