feat: enable dynamic routes on frontend

This commit is contained in:
Valerio Ageno
2024-06-02 20:32:53 +02:00
parent 7557df10f3
commit 7cfaf94146
4 changed files with 36 additions and 16 deletions
@@ -20,10 +20,11 @@ pub const AXUM_ENTRY_POINT: &str = r##"
// File automatically generated // File automatically generated
// Do not manually change it // Do not manually change it
use axum::extract::Request; use axum::extract::{Path, Request};
use axum::response::Html; use axum::response::Html;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use std::collections::HashMap;
use tuono_lib::{ssr, Ssr}; use tuono_lib::{ssr, Ssr};
use reqwest::Client; use reqwest::Client;
@@ -44,11 +45,11 @@ async fn main() {
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }
async fn catch_all(request: Request) -> Html<String> { async fn catch_all(Path(params): Path<HashMap<String, String>>, request: Request) -> Html<String> {
let pathname = &request.uri(); let pathname = &request.uri();
let headers = &request.headers(); let headers = &request.headers();
let req = tuono_lib::Request::new(pathname, headers); let req = tuono_lib::Request::new(pathname, headers, params);
// TODO: remove unwrap // TODO: remove unwrap
+12 -3
View File
@@ -3,10 +3,11 @@ use std::collections::HashMap;
use axum::http::{HeaderMap, Uri}; use axum::http::{HeaderMap, Uri};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Clone)]
pub struct Request<'a> { pub struct Request<'a> {
uri: &'a Uri, uri: &'a Uri,
headers: &'a HeaderMap, headers: &'a HeaderMap,
pub params: HashMap<String, String>,
} }
/// Location must match client side interface /// Location must match client side interface
@@ -21,8 +22,16 @@ pub struct Location<'a> {
} }
impl<'a> Request<'a> { impl<'a> Request<'a> {
pub fn new(uri: &'a Uri, headers: &'a HeaderMap) -> Request<'a> { pub fn new(
Request { uri, headers } uri: &'a Uri,
headers: &'a HeaderMap,
params: HashMap<String, String>,
) -> Request<'a> {
Request {
uri,
headers,
params,
}
} }
pub fn location(&self) -> Location<'a> { pub fn location(&self) -> Location<'a> {
+14 -5
View File
@@ -9,16 +9,21 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream {
quote! { quote! {
use axum::response::{Html, IntoResponse}; use axum::response::{Html, IntoResponse};
use axum::extract::State; use std::collections::HashMap;
use axum::extract::{State, Path};
use reqwest::Client; use reqwest::Client;
#item #item
pub async fn route(State(client): State<Client>, request: axum::extract::Request) -> Html<String> { pub async fn route(
Path(params): Path<HashMap<String, String>>,
State(client): State<Client>,
request: axum::extract::Request
) -> Html<String> {
let pathname = &request.uri(); let pathname = &request.uri();
let headers = &request.headers(); let headers = &request.headers();
let req = tuono_lib::Request::new(pathname, headers); let req = tuono_lib::Request::new(pathname, headers, params);
let local_response = #fn_name(req.clone(), client).await; let local_response = #fn_name(req.clone(), client).await;
@@ -40,11 +45,15 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream {
} }
} }
pub async fn api(State(client): State<Client>, request: axum::extract::Request) -> axum::response::Response { pub async fn api(
Path(params): Path<HashMap<String, String>>,
State(client): State<Client>,
request: axum::extract::Request
) -> axum::response::Response {
let pathname = &request.uri(); let pathname = &request.uri();
let headers = &request.headers(); let headers = &request.headers();
let req = tuono_lib::Request::new(pathname, headers); let req = tuono_lib::Request::new(pathname, headers, params);
let local_response = #fn_name(req.clone(), client).await; let local_response = #fn_name(req.clone(), client).await;
@@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from 'react' import { useState, useEffect, useRef } from 'react'
import type { Route } from '../route' import type { Route } from '../route'
import { useRouterStore } from './useRouterStore'
const isServer = typeof document === 'undefined' const isServer = typeof document === 'undefined'
@@ -20,6 +21,7 @@ export function useServerSideProps(
serverSideProps: any, serverSideProps: any,
): UseServerSidePropsReturn { ): UseServerSidePropsReturn {
const isFirstRendering = useRef<boolean>(true) const isFirstRendering = useRef<boolean>(true)
const location = useRouterStore((st) => st.location)
const [isLoading, setIsLoading] = useState<boolean>( const [isLoading, setIsLoading] = useState<boolean>(
// Force loading if has handler // Force loading if has handler
route.options.hasHandler && route.options.hasHandler &&
@@ -41,15 +43,14 @@ export function useServerSideProps(
return return
} }
// After client side routing load again the remote data // After client side routing load again the remote data
if (route.options.hasHandler && !data) { if (route.options.hasHandler) {
;(async (): Promise<void> => { ;(async (): Promise<void> => {
setIsLoading(true) setIsLoading(true)
try { try {
const path = route.path === '/' ? '' : route.path const res = await fetch(`/__tuono/data${location.pathname}`)
const res = await fetch(`/__tuono/data/${path}`)
setData(await res.json()) setData(await res.json())
} catch (error) { } catch (error) {
// Handle here error throw Error('Failed loading Server Side Data', { cause: error })
} finally { } finally {
setIsLoading(false) setIsLoading(false)
} }
@@ -60,7 +61,7 @@ export function useServerSideProps(
return (): void => { return (): void => {
setData(undefined) setData(undefined)
} }
}, [route.path]) }, [location.pathname])
return { isLoading, data } return { isLoading, data }
} }