diff --git a/packages/tuono-router/src/components/Matches.tsx b/packages/tuono-router/src/components/Matches.tsx index 8ae723bb..917e618e 100644 --- a/packages/tuono-router/src/components/Matches.tsx +++ b/packages/tuono-router/src/components/Matches.tsx @@ -3,7 +3,12 @@ import { useRouterStore } from '../hooks/useRouterStore' import { RouteMatch } from './RouteMatch' import NotFound from './NotFound' -export function Matches(): JSX.Element { +interface MatchesProps { + // user defined props + serverSideProps: any +} + +export function Matches({ serverSideProps }: MatchesProps): JSX.Element { const location = useRouterStore((st) => st.location) const router = useRouter() @@ -13,5 +18,5 @@ export function Matches(): JSX.Element { return } - return + return } diff --git a/packages/tuono-router/src/components/RouteMatch.tsx b/packages/tuono-router/src/components/RouteMatch.tsx index 50e5b802..c4c4398b 100644 --- a/packages/tuono-router/src/components/RouteMatch.tsx +++ b/packages/tuono-router/src/components/RouteMatch.tsx @@ -1,23 +1,28 @@ import type { Route } from '../route' +import { useServerSideProps } from '../hooks/useServerSideProps' interface MatchProps { route: Route + // User defined server side props + serverSideProps: any } /** * Returns the route match with the root element if exists + * + * It handles the fetch of the client side resources */ -export const RouteMatch = ({ route }: MatchProps): JSX.Element => { - if (route.options.hasHandler) { - console.log('Has rust handler') - } +export const RouteMatch = ({ + route, + serverSideProps, +}: MatchProps): JSX.Element => { + const { data, isLoading } = useServerSideProps(route, serverSideProps) if (!route.isRoot) { - console.log(route.options) return route.options.getParentRoute().component({ - children: route.options.component(), + children: route.options.component({ data, isLoading }), }) } - return route.options.component() + return route.options.component({ data, isLoading }) } diff --git a/packages/tuono-router/src/components/RouterContext.tsx b/packages/tuono-router/src/components/RouterContext.tsx index 1b0595a8..86973784 100644 --- a/packages/tuono-router/src/components/RouterContext.tsx +++ b/packages/tuono-router/src/components/RouterContext.tsx @@ -3,18 +3,18 @@ import type { Router } from '../router' const routerContext = React.createContext(null!) -const ROUTER_DOM_CONTEXT = '__TUONO_ROUTER__CONTEXT__' +const TUONO_CONTEXT = '__TUONO_CONTEXT__' export function getRouterContext(): any { if (typeof document === 'undefined') { return routerContext } - if (window[ROUTER_DOM_CONTEXT as any]) { - return window[ROUTER_DOM_CONTEXT as any] + if (window[TUONO_CONTEXT as any]) { + return window[TUONO_CONTEXT as any] } - window[ROUTER_DOM_CONTEXT as any] = routerContext as any + window[TUONO_CONTEXT as any] = routerContext as any return routerContext } diff --git a/packages/tuono-router/src/components/RouterProvider.tsx b/packages/tuono-router/src/components/RouterProvider.tsx index 0c084cea..71064d53 100644 --- a/packages/tuono-router/src/components/RouterProvider.tsx +++ b/packages/tuono-router/src/components/RouterProvider.tsx @@ -45,6 +45,7 @@ interface RouterProviderProps { interface ServerProps { router: Location + props: any } const initRouterStore = (props?: ServerProps): void => { @@ -74,13 +75,12 @@ const initRouterStore = (props?: ServerProps): void => { export function RouterProvider({ router, serverProps, - ...rest }: RouterProviderProps): JSX.Element { initRouterStore(serverProps) return ( - - + + ) } diff --git a/packages/tuono-router/src/hooks/useServerSideProps.tsx b/packages/tuono-router/src/hooks/useServerSideProps.tsx new file mode 100644 index 00000000..d57a27cb --- /dev/null +++ b/packages/tuono-router/src/hooks/useServerSideProps.tsx @@ -0,0 +1,63 @@ +import { useState, useEffect, useRef } from 'react' +import type { Route } from '../route' + +const isServer = typeof document === 'undefined' + +interface UseServerSidePropsReturn { + data: any + isLoading: boolean +} +/* + * Use the props provided by the SSR and dehydrate the + * props for client side usage. + * + * In case is a client fetch the remote data API + */ +export function useServerSideProps( + route: Route, + serverSideProps: any, +): UseServerSidePropsReturn { + const isFirstRendering = useRef(true) + const [isLoading, setIsLoading] = useState( + // Force loading if has handler + route.options.hasHandler && + // Avoid loading on the server + !isServer && + // Avoid loading if first rendering + !isFirstRendering.current, + ) + + const [data, setData] = useState( + serverSideProps || window.__TUONO_SSR_PROPS__.props, + ) + + useEffect(() => { + // First loading just dehydrate since the + // props are already bundled by the SSR + if (isFirstRendering.current) { + isFirstRendering.current = false + return + } + // After client side routing load again the remote data + if (route.options.hasHandler && !data) { + ;(async (): Promise => { + setIsLoading(true) + try { + const res = await fetch(`/__tuono/data/${route.path}`) + setData(await res.json()) + } catch (error) { + // Handle here error + } finally { + setIsLoading(false) + } + })() + } + + // Clean up the data when changing route + return (): void => { + setData(undefined) + } + }, [route.path]) + + return { isLoading, data } +} diff --git a/packages/tuono-router/src/index.tsx b/packages/tuono-router/src/index.tsx index 84ec4f37..28266a55 100644 --- a/packages/tuono-router/src/index.tsx +++ b/packages/tuono-router/src/index.tsx @@ -1,3 +1,8 @@ +declare global { + interface Window { + __TUONO_SSR__PROPS__: any + } +} export { RouterProvider } from './components/RouterProvider' export { default as Link } from './components/Link' export { createRouter } from './router'