main
js 95 lines 2.53 KB
Raw
1 import React from 'react';
2 import {useContext, useMemo, useRef, useState, useLayoutEffect} from 'react';
3 import {__RouterContext} from 'react-router';
4 import {ReactReduxContext} from 'react-redux';
5
6 import ThemeContext from './shared/ThemeContext';
7
8 let rendererModule = {
9 status: 'pending',
10 promise: null,
11 result: null,
12 };
13
14 export default function lazyLegacyRoot(getLegacyComponent) {
15 let componentModule = {
16 status: 'pending',
17 promise: null,
18 result: null,
19 };
20
21 return function Wrapper(props) {
22 const createLegacyRoot = readModule(
23 rendererModule,
24 () => import('../legacy/createLegacyRoot')
25 ).default;
26 const Component = readModule(componentModule, getLegacyComponent).default;
27 const containerRef = useRef(null);
28 const rootRef = useRef(null);
29
30 // Populate every contexts we want the legacy subtree to see.
31 // Then in src/legacy/createLegacyRoot we will apply them.
32 const theme = useContext(ThemeContext);
33 const router = useContext(__RouterContext);
34 const reactRedux = useContext(ReactReduxContext);
35 const context = useMemo(
36 () => ({
37 theme,
38 router,
39 reactRedux,
40 }),
41 [theme, router, reactRedux]
42 );
43
44 // Create/unmount.
45 useLayoutEffect(() => {
46 if (!rootRef.current) {
47 rootRef.current = createLegacyRoot(containerRef.current);
48 }
49 const root = rootRef.current;
50 return () => {
51 root.unmount();
52 };
53 }, [createLegacyRoot]);
54
55 // Mount/update.
56 useLayoutEffect(() => {
57 if (rootRef.current) {
58 rootRef.current.render(Component, props, context);
59 }
60 }, [Component, props, context]);
61
62 return <div style={{display: 'contents'}} ref={containerRef} />;
63 };
64 }
65
66 // This is similar to React.lazy, but implemented manually.
67 // We use this to Suspend rendering of this component until
68 // we fetch the component and the legacy React to render it.
69 function readModule(record, createPromise) {
70 if (record.status === 'fulfilled') {
71 return record.result;
72 }
73 if (record.status === 'rejected') {
74 throw record.result;
75 }
76 if (!record.promise) {
77 record.promise = createPromise().then(
78 value => {
79 if (record.status === 'pending') {
80 record.status = 'fulfilled';
81 record.promise = null;
82 record.result = value;
83 }
84 },
85 error => {
86 if (record.status === 'pending') {
87 record.status = 'rejected';
88 record.promise = null;
89 record.result = error;
90 }
91 }
92 );
93 }
94 throw record.promise;
95 }