-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
renderClient.tsx
134 lines (124 loc) · 3.84 KB
/
renderClient.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import * as ReactDOM from 'react-dom';
import React, { useEffect } from 'react';
import { ApplyPluginsType, Plugin, Router } from '@umijs/runtime';
import { matchRoutes } from 'react-router-config';
import { IRoute } from '..';
import renderRoutes from '../renderRoutes/renderRoutes';
interface IRouterComponentProps {
routes: IRoute[];
plugin: Plugin;
history: any;
ssrProps?: object;
defaultTitle?: string;
dynamicImport?: boolean;
isServer?: boolean;
}
interface IOpts extends IRouterComponentProps {
rootElement?: string | HTMLElement;
callback?: () => void;
}
function RouterComponent(props: IRouterComponentProps) {
const { history, ...renderRoutesProps } = props;
useEffect(() => {
// first time using window.g_initialProps
// switch route fetching data, if exact route reset window.getInitialProps
if ((window as any).g_initialProps) {
(window as any).g_initialProps = null;
}
function routeChangeHandler(location: any, action?: string) {
const matchedRoutes = matchRoutes(props.routes, location.pathname);
// Set title
if (
typeof document !== 'undefined' &&
renderRoutesProps.defaultTitle !== undefined
) {
document.title =
(matchedRoutes.length &&
// @ts-ignore
matchedRoutes[matchedRoutes.length - 1].route.title) ||
renderRoutesProps.defaultTitle ||
'';
}
props.plugin.applyPlugins({
key: 'onRouteChange',
type: ApplyPluginsType.event,
args: {
routes: props.routes,
matchedRoutes,
location,
action,
},
});
}
routeChangeHandler(history.location, 'POP');
return history.listen(routeChangeHandler);
}, [history]);
return <Router history={history}>{renderRoutes(renderRoutesProps)}</Router>;
}
/**
* preload for SSR in dynamicImport
* exec preload Promise function before ReactDOM.hydrate
* @param Routes
*/
export async function preloadComponent(
readyRoutes: IRoute[],
pathname = window.location.pathname,
): Promise<IRoute[]> {
// using matched routes not load all routes
const matchedRoutes = matchRoutes(readyRoutes, pathname);
for (const matchRoute of matchedRoutes) {
const route = matchRoute.route as IRoute;
// load all preload function, because of only a chance to load
if (route.component?.preload) {
const preloadComponent = await route.component.preload();
route.component = preloadComponent.default || preloadComponent;
}
if (route.routes) {
route.routes = await preloadComponent(route.routes, pathname);
}
}
return readyRoutes;
}
export default function renderClient(opts: IOpts) {
const rootContainer = opts.plugin.applyPlugins({
type: ApplyPluginsType.modify,
key: 'rootContainer',
initialValue: (
<RouterComponent
history={opts.history}
routes={opts.routes}
plugin={opts.plugin}
ssrProps={opts.ssrProps}
defaultTitle={opts.defaultTitle}
/>
),
args: {
history: opts.history,
routes: opts.routes,
plugin: opts.plugin,
},
});
if (opts.rootElement) {
const rootElement =
typeof opts.rootElement === 'string'
? document.getElementById(opts.rootElement)
: opts.rootElement;
const callback = opts.callback || (() => {});
// flag showing SSR successed
if (window.g_useSSR) {
if (opts.dynamicImport) {
// dynamicImport should preload current route component
// first loades);
preloadComponent(opts.routes).then(function () {
ReactDOM.hydrate(rootContainer, rootElement, callback);
});
} else {
ReactDOM.hydrate(rootContainer, rootElement, callback);
}
} else {
ReactDOM.render(rootContainer, rootElement, callback);
}
} else {
return rootContainer;
}
}