@samitouri / QOS-React / commits / 41aa345d2b

Fix a crash in Suspense with findDOMNode

Dan Abramov committed Apr 3, 2019 at 13:21 UTC 41aa345d2bde261c2fb4a4ef89e379640c88be67
2 files changed +67 -2
packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
+44
@@ -233,4 +233,48 @@ describe('ReactDOMSuspensePlaceholder', () => {
233 await Lazy;
234 expect(log).toEqual(['cDU first', 'cDU second']);
235 });
236 +
237 + // Regression test for https://github.com/facebook/react/issues/14188
238 + it('can call findDOMNode() in a suspended component commit phase (#2)', () => {
239 + let suspendOnce = Promise.resolve();
240 + function Suspend() {
241 + if (suspendOnce) {
242 + let promise = suspendOnce;
243 + suspendOnce = null;
244 + throw promise;
245 + }
246 + return null;
247 + }
248 +
249 + const log = [];
250 + class Child extends React.Component {
251 + componentDidMount() {
252 + log.push('cDM');
253 + ReactDOM.findDOMNode(this);
254 + }
255 +
256 + componentDidUpdate() {
257 + log.push('cDU');
258 + ReactDOM.findDOMNode(this);
259 + }
260 +
261 + render() {
262 + return null;
263 + }
264 + }
265 +
266 + function App() {
267 + return (
268 + <Suspense fallback="Loading">
269 + <Suspend />
270 + <Child />
271 + </Suspense>
272 + );
273 + }
274 +
275 + ReactDOM.render(<App />, container);
276 + expect(log).toEqual(['cDM']);
277 + ReactDOM.render(<App />, container);
278 + expect(log).toEqual(['cDM', 'cDU']);
279 + });
280 });
packages/react-reconciler/src/ReactFiberTreeReflection.js
+23 -2
@@ -21,6 +21,8 @@ import {
21 HostRoot,
22 HostPortal,
23 HostText,
24 + Fragment,
25 + SuspenseComponent,
26 } from 'shared/ReactWorkTags';
27 import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
28
@@ -119,8 +121,27 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
121 let parentA = a.return;
122 let parentB = parentA ? parentA.alternate : null;
123 if (!parentA || !parentB) {
122 - // We're at the root.
123 - break;
124 + // We're either at the root, or we're in a special Fragment
125 + // with no alternate, which is how Suspense (un)hiding works.
126 + let maybeSuspenseFragment = parentA || parentB;
127 + if (maybeSuspenseFragment && maybeSuspenseFragment.tag === Fragment) {
128 + const maybeSuspense = maybeSuspenseFragment.return;
129 + if (
130 + maybeSuspense &&
131 + maybeSuspense.tag === SuspenseComponent &&
132 + // If state isn't null, it timed out and we have two Fragment children.
133 + maybeSuspense.memoizedState !== null
134 + ) {
135 + parentA = maybeSuspense;
136 + parentB = maybeSuspense;
137 + a = maybeSuspenseFragment;
138 + b = maybeSuspenseFragment;
139 + } else {
140 + break;
141 + }
142 + } else {
143 + break;
144 + }
145 }
146
147 // If both copies of the parent fiber point to the same child, we can