27
let container;
28
29
beforeEach(() => {
30
- jest.resetModules();
30
container = document.createElement('div');
31
React = require('react');
32
ReactDOM = require('react-dom');
39
assertLog = InternalTestUtils.assertLog;
40
41
document.body.appendChild(container);
42
+ window.event = undefined;
43
});
44
45
afterEach(() => {
566
567
expect(container.textContent).toBe('new');
568
});
569
+
570
+ it('should synchronously render the transition lane scheduled in a popState', async () => {
571
+ function App() {
572
+ const [syncState, setSyncState] = React.useState(false);
573
+ const [hasNavigated, setHasNavigated] = React.useState(false);
574
+ function onPopstate() {
575
+ Scheduler.log(`popState`);
576
+ React.startTransition(() => {
577
+ setHasNavigated(true);
578
+ });
579
+ setSyncState(true);
580
+ }
581
+ React.useEffect(() => {
582
+ window.addEventListener('popstate', onPopstate);
583
+ return () => {
584
+ window.removeEventListener('popstate', onPopstate);
585
+ };
586
+ }, []);
587
+ Scheduler.log(`render:${hasNavigated}/${syncState}`);
588
+ return null;
589
+ }
590
+ const root = ReactDOMClient.createRoot(container);
591
+ await act(async () => {
592
+ root.render(<App />);
593
+ });
594
+ assertLog(['render:false/false']);
595
+
596
+ await act(async () => {
597
+ const popStateEvent = new Event('popstate');
598
+ // Jest is not emulating window.event correctly in the microtask
599
+ window.event = popStateEvent;
600
+ window.dispatchEvent(popStateEvent);
601
+ queueMicrotask(() => {
602
+ window.event = undefined;
603
+ });
604
+ });
605
+
606
+ assertLog(['popState', 'render:true/true']);
607
+ await act(() => {
608
+ root.unmount();
609
+ });
610
+ });
611
+
612
+ it('Should not flush transition lanes if there is no transition scheduled in popState', async () => {
613
+ let setHasNavigated;
614
+ function App() {
615
+ const [syncState, setSyncState] = React.useState(false);
616
+ const [hasNavigated, _setHasNavigated] = React.useState(false);
617
+ setHasNavigated = _setHasNavigated;
618
+ function onPopstate() {
619
+ setSyncState(true);
620
+ }
621
+
622
+ React.useEffect(() => {
623
+ window.addEventListener('popstate', onPopstate);
624
+ return () => {
625
+ window.removeEventListener('popstate', onPopstate);
626
+ };
627
+ }, []);
628
+
629
+ Scheduler.log(`render:${hasNavigated}/${syncState}`);
630
+ return null;
631
+ }
632
+ const root = ReactDOMClient.createRoot(container);
633
+ await act(async () => {
634
+ root.render(<App />);
635
+ });
636
+ assertLog(['render:false/false']);
637
+
638
+ React.startTransition(() => {
639
+ setHasNavigated(true);
640
+ });
641
+ await act(async () => {
642
+ const popStateEvent = new Event('popstate');
643
+ // Jest is not emulating window.event correctly in the microtask
644
+ window.event = popStateEvent;
645
+ window.dispatchEvent(popStateEvent);
646
+ queueMicrotask(() => {
647
+ window.event = undefined;
648
+ });
649
+ });
650
+ assertLog(['render:false/true', 'render:true/true']);
651
+ await act(() => {
652
+ root.unmount();
653
+ });
654
+ });
655
+
656
+ it('transition lane in popState should yield if it suspends', async () => {
657
+ const never = {then() {}};
658
+ let _setText;
659
+
660
+ function App() {
661
+ const [shouldSuspend, setShouldSuspend] = React.useState(false);
662
+ const [text, setText] = React.useState('0');
663
+ _setText = setText;
664
+ if (shouldSuspend) {
665
+ Scheduler.log('Suspend!');
666
+ throw never;
667
+ }
668
+ function onPopstate() {
669
+ React.startTransition(() => {
670
+ setShouldSuspend(val => !val);
671
+ });
672
+ }
673
+ React.useEffect(() => {
674
+ window.addEventListener('popstate', onPopstate);
675
+ return () => window.removeEventListener('popstate', onPopstate);
676
+ }, []);
677
+ Scheduler.log(`Child:${shouldSuspend}/${text}`);
678
+ return text;
679
+ }
680
+
681
+ const root = ReactDOMClient.createRoot(container);
682
+ await act(async () => {
683
+ root.render(<App />);
684
+ });
685
+ assertLog(['Child:false/0']);
686
+
687
+ await act(() => {
688
+ const popStateEvent = new Event('popstate');
689
+ window.event = popStateEvent;
690
+ window.dispatchEvent(popStateEvent);
691
+ queueMicrotask(() => {
692
+ window.event = undefined;
693
+ });
694
+ });
695
+ assertLog(['Suspend!']);
696
+
697
+ await act(async () => {
698
+ _setText('1');
699
+ });
700
+ assertLog(['Child:false/1', 'Suspend!']);
701
+
702
+ root.unmount();
703
+ });
704
});