diff --git a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
index de43fd44274c3..45fec880410c4 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
@@ -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 (
+
+
+
+
+ );
+ }
+
+ ReactDOM.render(, container);
+ expect(log).toEqual(['cDM']);
+ ReactDOM.render(, container);
+ expect(log).toEqual(['cDM', 'cDU']);
+ });
});
diff --git a/packages/react-reconciler/src/ReactFiberTreeReflection.js b/packages/react-reconciler/src/ReactFiberTreeReflection.js
index f2c3e37b699ec..150e8200aed40 100644
--- a/packages/react-reconciler/src/ReactFiberTreeReflection.js
+++ b/packages/react-reconciler/src/ReactFiberTreeReflection.js
@@ -21,6 +21,8 @@ import {
HostRoot,
HostPortal,
HostText,
+ Fragment,
+ SuspenseComponent,
} from 'shared/ReactWorkTags';
import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
@@ -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