Bug: Spawning hydration in response to Idle update (#19011)
* Bug: Spawning hydration in response to Idle update Adds a test that fails in the new fork. * Fix typos related to Idle priority These are just silly mistakes that weren't caught by any of our tests. There's a lot of duplication in the Lanes module right now. It's also not super stable as we continue to refine our heuristics. Hopefully the final state is simpler and less prone to these types of mistakes.
Andrew Clark committed
May 26, 2020 at 18:53 UTC
18de3b6e7c8a93289d9df67339d876bba6ff3e0c
2 files changed
+76
-1
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+74
@@ -587,6 +587,80 @@ describe('ReactDOMServerPartialHydration', () => {
587
expect(span.className).toBe('hi');
588
});
589
590
+ // @gate experimental
591
+ it('blocks updates to hydrate the content first if props changed at idle priority', async () => {
592
+ let suspend = false;
593
+ let resolve;
594
+ const promise = new Promise(resolvePromise => (resolve = resolvePromise));
595
+ const ref = React.createRef();
596
+
597
+ function Child({text}) {
598
+ if (suspend) {
599
+ throw promise;
600
+ } else {
601
+ return text;
602
+ }
603
+ }
604
+
605
+ function App({text, className}) {
606
+ return (
607
+ <div>
608
+ <Suspense fallback="Loading...">
609
+ <span ref={ref} className={className}>
610
+ <Child text={text} />
611
+ </span>
612
+ </Suspense>
613
+ </div>
614
+ );
615
+ }
616
+
617
+ suspend = false;
618
+ const finalHTML = ReactDOMServer.renderToString(
619
+ <App text="Hello" className="hello" />,
620
+ );
621
+ const container = document.createElement('div');
622
+ container.innerHTML = finalHTML;
623
+
624
+ const span = container.getElementsByTagName('span')[0];
625
+
626
+ // On the client we don't have all data yet but we want to start
627
+ // hydrating anyway.
628
+ suspend = true;
629
+ const root = ReactDOM.createRoot(container, {hydrate: true});
630
+ root.render(<App text="Hello" className="hello" />);
631
+ Scheduler.unstable_flushAll();
632
+ jest.runAllTimers();
633
+
634
+ expect(ref.current).toBe(null);
635
+ expect(span.textContent).toBe('Hello');
636
+
637
+ // Schedule an update at idle priority
638
+ Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () => {
639
+ root.render(<App text="Hi" className="hi" />);
640
+ });
641
+
642
+ // At the same time, resolving the promise so that rendering can complete.
643
+ suspend = false;
644
+ resolve();
645
+ await promise;
646
+
647
+ // This should first complete the hydration and then flush the update onto the hydrated state.
648
+ Scheduler.unstable_flushAll();
649
+ jest.runAllTimers();
650
+
651
+ // The new span should be the same since we should have successfully hydrated
652
+ // before changing it.
653
+ const newSpan = container.getElementsByTagName('span')[0];
654
+ expect(span).toBe(newSpan);
655
+
656
+ // We should now have fully rendered with a ref on the new span.
657
+ expect(ref.current).toBe(span);
658
+ expect(span.textContent).toBe('Hi');
659
+ // If we ended up hydrating the existing content, we won't have properly
660
+ // patched up the tree, which might mean we haven't patched the className.
661
+ expect(span.className).toBe('hi');
662
+ });
663
+
664
// @gate experimental
665
it('shows the fallback if props have changed before hydration completes and is still suspended', async () => {
666
let suspend = false;
packages/react-reconciler/src/ReactFiberLane.js
+2
-1
@@ -208,6 +208,7 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
208
return_updateRangeEnd = IdleUpdateRangeStart;
209
return IdleHydrationLane;
210
} else {
211
+ return_highestLanePriority = IdleLanePriority;
212
return_updateRangeEnd = IdleUpdateRangeEnd;
213
return idleLanes;
214
}
@@ -527,7 +528,7 @@ export function findUpdateLane(
528
// Should be handled by findTransitionLane instead
529
break;
530
case IdleLanePriority:
530
- let lane = findLane(IdleUpdateRangeStart, IdleUpdateRangeEnd, IdleLanes);
531
+ let lane = findLane(IdleUpdateRangeStart, IdleUpdateRangeEnd, wipLanes);
532
if (lane === NoLane) {
533
lane = IdleHydrationLane;
534
}