@samitouri / QOS-React-2 / commits / 8cb2fb21eb

Refine isFiberSuspenseAndTimedOut (#18184)

Dominic Gannaway committed Mar 2, 2020 at 14:15 UTC 8cb2fb21eb4e931b80d8b0502059061891ac4563
2 files changed +76 -2
packages/react-reconciler/src/ReactFiberScope.js
+6 -1
@@ -27,7 +27,12 @@ import {
27 import {enableScopeAPI} from 'shared/ReactFeatureFlags';
28
29 function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
30 - return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
30 + const memoizedState = fiber.memoizedState;
31 + return (
32 + fiber.tag === SuspenseComponent &&
33 + memoizedState !== null &&
34 + memoizedState.dehydrated === null
35 + );
36 }
37
38 function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+70 -1
@@ -13,6 +13,8 @@ import {createEventTarget} from 'dom-event-testing-library';
13
14 let React;
15 let ReactFeatureFlags;
16 +let ReactDOMServer;
17 +let Scheduler;
18
19 describe('ReactScope', () => {
20 beforeEach(() => {
@@ -21,6 +23,7 @@ describe('ReactScope', () => {
23 ReactFeatureFlags.enableScopeAPI = true;
24 ReactFeatureFlags.enableDeprecatedFlareAPI = true;
25 React = require('react');
26 + Scheduler = require('scheduler');
27 });
28
29 if (!__EXPERIMENTAL__) {
@@ -34,6 +37,7 @@ describe('ReactScope', () => {
37
38 beforeEach(() => {
39 ReactDOM = require('react-dom');
40 + ReactDOMServer = require('react-dom/server');
41 container = document.createElement('div');
42 document.body.appendChild(container);
43 });
@@ -208,7 +212,6 @@ describe('ReactScope', () => {
212
213 it('scopes support server-side rendering and hydration', () => {
214 const TestScope = React.unstable_createScope();
211 - const ReactDOMServer = require('react-dom/server');
215 const scopeRef = React.createRef();
216 const divRef = React.createRef();
217 const spanRef = React.createRef();
@@ -306,6 +309,72 @@ describe('ReactScope', () => {
309 ReactDOM.render(null, container);
310 expect(scopeRef.current).toBe(null);
311 });
312 +
313 + it('correctly works with suspended boundaries that are hydrated', async () => {
314 + let suspend = false;
315 + let resolve;
316 + const promise = new Promise(resolvePromise => (resolve = resolvePromise));
317 + const ref = React.createRef();
318 + const TestScope = React.unstable_createScope();
319 + const scopeRef = React.createRef();
320 + const testScopeQuery = (type, props) => true;
321 +
322 + function Child() {
323 + if (suspend) {
324 + throw promise;
325 + } else {
326 + return 'Hello';
327 + }
328 + }
329 +
330 + function App() {
331 + return (
332 + <div>
333 + <TestScope ref={scopeRef}>
334 + <React.Suspense fallback="Loading...">
335 + <span ref={ref}>
336 + <Child />
337 + </span>
338 + </React.Suspense>
339 + </TestScope>
340 + </div>
341 + );
342 + }
343 +
344 + // First we render the final HTML. With the streaming renderer
345 + // this may have suspense points on the server but here we want
346 + // to test the completed HTML. Don't suspend on the server.
347 + suspend = false;
348 + let finalHTML = ReactDOMServer.renderToString(<App />);
349 +
350 + let container2 = document.createElement('div');
351 + container2.innerHTML = finalHTML;
352 +
353 + let span = container2.getElementsByTagName('span')[0];
354 +
355 + // On the client we don't have all data yet but we want to start
356 + // hydrating anyway.
357 + suspend = true;
358 + let root = ReactDOM.createRoot(container2, {hydrate: true});
359 + root.render(<App />);
360 + Scheduler.unstable_flushAll();
361 + jest.runAllTimers();
362 +
363 + // This should not cause a runtime exception, see:
364 + // https://github.com/facebook/react/pull/18184
365 + scopeRef.current.DO_NOT_USE_queryAllNodes(testScopeQuery);
366 + expect(ref.current).toBe(null);
367 +
368 + // Resolving the promise should continue hydration
369 + suspend = false;
370 + resolve();
371 + await promise;
372 + Scheduler.unstable_flushAll();
373 + jest.runAllTimers();
374 +
375 + // We should now have hydrated with a ref on the existing span.
376 + expect(ref.current).toBe(span);
377 + });
378 });
379
380 describe('ReactTestRenderer', () => {