From 10ed8b6304b8384cfafb3fb839e295cf2a1ccf51 Mon Sep 17 00:00:00 2001 From: Andrey Polischuk Date: Wed, 13 Dec 2023 10:49:38 +0300 Subject: [PATCH 1/3] fix: preload server route before hydration --- src/client/stream.tsx | 14 +++++++++++++- src/common/types.ts | 1 + src/components/lazy.tsx | 6 ++++-- src/components/loader.tsx | 22 ++++++++++++++++++---- src/components/routes.tsx | 2 +- src/components/state.tsx | 6 ++++-- src/server/stream.tsx | 1 - 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/client/stream.tsx b/src/client/stream.tsx index 7154f68..7d1efab 100644 --- a/src/client/stream.tsx +++ b/src/client/stream.tsx @@ -4,6 +4,7 @@ import {BrowserRouter} from 'react-router-dom' import {RenderOptions, TransitionMode} from '../common/types' import {getState} from '../components/state' import {AppContextProvider} from '../components/context' +import {matchRoute} from '../components/loader' import {Routes} from '../components/routes' import {Layout as BaseLayout} from '../components/layout' import {Document as BaseDocument} from '../components/document' @@ -33,7 +34,8 @@ export interface HydrateFromStreamOptions extends RenderOptions { export const hydrateFromStream = async ( options: HydrateFromStreamOptions ): Promise => { - const state = getState() + const {pathname, ...state} = getState() + const { routes, Layout = BaseLayout, @@ -64,5 +66,15 @@ export const hydrateFromStream = async ( ) + const match = matchRoute({pathname, routes}) + + if (match) { + const { + route: {Component} + } = match + + await Component.preload?.() + } + hydrateRoot(document, app) } diff --git a/src/common/types.ts b/src/common/types.ts index 6e16725..b51b967 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -88,6 +88,7 @@ export type PageComponent

= React.ComponentType< /** Lazy page component */ export interface LazyPageComponent

extends React.LazyExoticComponent> { + preload: Loader getMetaData: GetMetaData getInitialData: GetInitialData } diff --git a/src/components/lazy.tsx b/src/components/lazy.tsx index 70798f7..3357b03 100644 --- a/src/components/lazy.tsx +++ b/src/components/lazy.tsx @@ -41,12 +41,12 @@ export const lazy = (componentFactory: ComponentFactory): LazyPageComponent => { } function loaderFactory( - dataFactory: DataFactory + dataFactory?: DataFactory ): Loader { return async (context: Context & C) => { const {default: Component} = await onceFactory() - return dataFactory(Component, context) ?? Promise.resolve() + return dataFactory?.(Component, context) } } @@ -60,5 +60,7 @@ export const lazy = (componentFactory: ComponentFactory): LazyPageComponent => { ({getInitialData}, context) => getInitialData?.(context) ) + Component.preload = loaderFactory() + return Component } diff --git a/src/components/loader.tsx b/src/components/loader.tsx index a1710b6..4d1ce1b 100644 --- a/src/components/loader.tsx +++ b/src/components/loader.tsx @@ -1,13 +1,27 @@ import {matchRoutes, type RouteMatch} from 'react-router-dom' import {Context, PageRoute, InitialData, MetaData} from '../common/types' -/** Route loader options */ -export interface LoadRouteDataOptions { - context: Context +/** Match route options */ +export interface MatchRouteOptions { pathname: string routes: PageRoute[] } +/** Match route */ +export const matchRoute = ({ + pathname, + routes +}: MatchRouteOptions): RouteMatch => { + const [match] = matchRoutes(routes, pathname) ?? [] + + return match +} + +/** Route loader options */ +export interface LoadRouteDataOptions extends MatchRouteOptions { + context: Context +} + /** Route data */ export interface RouteData { data?: InitialData @@ -21,7 +35,7 @@ export const loadRouteData = async ({ routes, context }: LoadRouteDataOptions): Promise => { - const [match] = matchRoutes(routes, pathname) ?? [] + const match = matchRoute({pathname, routes}) if (!match) { return { diff --git a/src/components/routes.tsx b/src/components/routes.tsx index 6e79fcf..e4a0d45 100644 --- a/src/components/routes.tsx +++ b/src/components/routes.tsx @@ -89,7 +89,7 @@ export const Routes: React.FC = ({ key={path} path={path} element={ - : null}> + : undefined}> {isWaitingMode && routeData.isLoading && Fallback ? ( ) : ( diff --git a/src/components/state.tsx b/src/components/state.tsx index dc0e641..2d972bf 100644 --- a/src/components/state.tsx +++ b/src/components/state.tsx @@ -38,7 +38,7 @@ export interface StateProps { * ``` */ export const State: React.FC = ({name = STATE_NAME, state}) => { - const {data, meta, styles, scripts} = useAppContext() + const {data, meta, styles, scripts, req} = useAppContext() if (!isSSR) { return @@ -49,7 +49,9 @@ export const State: React.FC = ({name = STATE_NAME, state}) => { id={`__${name.toUpperCase()}__`} type="application/json" dangerouslySetInnerHTML={{ - __html: stringify(state ?? {data, meta, styles, scripts}) + __html: stringify( + state ?? {data, meta, styles, scripts, pathname: req?.path} + ) }} /> ) diff --git a/src/server/stream.tsx b/src/server/stream.tsx index ae33525..4c29c66 100644 --- a/src/server/stream.tsx +++ b/src/server/stream.tsx @@ -80,7 +80,6 @@ export const renderToStream = async ( pathname, search } as Location, - ...rest } From 10c1a21899c1c6a98aaebf974f93f9e3b7de4e64 Mon Sep 17 00:00:00 2001 From: Andrey Polischuk Date: Wed, 13 Dec 2023 10:52:50 +0300 Subject: [PATCH 2/3] docs: update transition mode --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 69ac6b4..2f43090 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,9 @@ hydrateFromStream({ By default, router uses `blocked` transition mode, and will wait for `getInitialData` to get completed to show the next page. -If your want show the next page with spinner or skeleton while `getInitialData` is pending, use `instant` transition mode. +If your want show the next page with spinner or skeleton while `getInitialData` is pending, use `wait-for-data` transition mode. + +And if your want show the next page with spinner or skeleton only while lazy page is pending, use `instant` transition mode. ```ts // src/routes.ts From 2be80559392cd2a902f21cfd86aa9f10d9952b37 Mon Sep 17 00:00:00 2001 From: Andrey Polischuk Date: Wed, 13 Dec 2023 10:54:01 +0300 Subject: [PATCH 3/3] chore: bump size limits --- .size-limit.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.size-limit.json b/.size-limit.json index 7e32f8e..4fffbe6 100644 --- a/.size-limit.json +++ b/.size-limit.json @@ -1,10 +1,10 @@ [ { "path": "dist/client/index.js", - "limit": "7.01 KB" + "limit": "7.1 KB" }, { "path": "dist/server/index.js", - "limit": "7.02 KB" + "limit": "7.1 KB" } ]