feature: add isomorphic `<Redirect/>` component (closes #412) (#466)

This commit is contained in:
Greg Johnston 2023-02-04 10:02:17 -05:00 committed by GitHub
parent 45275ff8d4
commit 4034aa9c11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 2 deletions

View File

@ -1,4 +1,4 @@
use leptos::{component, Scope, IntoView, view};
use leptos::{component, view, IntoView, Scope};
use leptos_router::*;
#[component]
@ -6,7 +6,7 @@ pub fn Nav(cx: Scope) -> impl IntoView {
view! { cx,
<header class="header">
<nav class="inner">
<A href="/">
<A href="/home">
<strong>"HN"</strong>
</A>
<A href="/new">

View File

@ -20,6 +20,7 @@ pub fn RouterExample(cx: Scope) -> impl IntoView {
<A exact=true href="/">"Contacts"</A>
<A href="about">"About"</A>
<A href="settings">"Settings"</A>
<A href="redirect-home">"Redirect to Home"</A>
</nav>
<main>
<Routes>
@ -44,6 +45,10 @@ pub fn RouterExample(cx: Scope) -> impl IntoView {
path="settings"
view=move |cx| view! { cx, <Settings/> }
/>
<Route
path="redirect-home"
view=move |cx| view! { cx, <Redirect path="/"/> }
/>
</Routes>
</main>
</Router>

View File

@ -438,6 +438,7 @@ fn provide_contexts(cx: leptos::Scope, req: &HttpRequest, res_options: ResponseO
provide_context(cx, MetaContext::new());
provide_context(cx, res_options);
provide_context(cx, req.clone());
provide_server_redirect(cx, move |path| redirect(cx, path));
}
fn leptos_corrected_path(req: &HttpRequest) -> String {

View File

@ -447,6 +447,7 @@ where
provide_context(cx, MetaContext::new());
provide_context(cx, req_parts);
provide_context(cx, default_res_options);
provide_server_redirect(cx, move |path| redirect(cx, path));
app_fn(cx).into_view(cx)
}
};

View File

@ -22,6 +22,7 @@ percent-encoding = "2"
thiserror = "1"
serde_urlencoded = "0.7"
serde = "1"
tracing = "0.1"
js-sys = { version = "0.3" }
wasm-bindgen = { version = "0.2" }
wasm-bindgen-futures = { version = "0.4" }

View File

@ -1,6 +1,7 @@
mod form;
mod link;
mod outlet;
mod redirect;
mod route;
mod router;
mod routes;
@ -8,6 +9,7 @@ mod routes;
pub use form::*;
pub use link::*;
pub use outlet::*;
pub use redirect::*;
pub use route::*;
pub use router::*;
pub use routes::*;

View File

@ -0,0 +1,65 @@
use crate::{use_navigate, use_resolved_path, NavigateOptions};
use leptos::{component, provide_context, use_context, IntoView, Scope};
use std::rc::Rc;
/// Redirects the user to a new URL, whether on the client side or on the server
/// side. If rendered on the server, this sets a `302` status code and sets a `Location`
/// header. If rendered in the browser, it uses client-side navigation to redirect.
/// In either case, it resolves the route relative to the current route. (To use
/// an absolute path, prefix it with `/`).
///
/// **Note**: Support for server-side redirects is provided by the server framework
/// integrations (`leptos_actix` and `leptos_axum`). If youre not using one of those
/// integrations, you should manually provide a way of redirecting on the server
/// using [provide_server_redirect].
#[component]
pub fn Redirect<P>(
cx: Scope,
/// The relative path to which the user should be redirected.
path: P,
/// Navigation options to be used on the client side.
#[prop(optional)]
options: Option<NavigateOptions>,
) -> impl IntoView
where
P: std::fmt::Display + 'static,
{
// resolve relative path
let path = use_resolved_path(cx, move || path.to_string());
let path = path.get().unwrap_or_else(|| "/".to_string());
// redirect on the server
if let Some(redirect_fn) = use_context::<ServerRedirectFunction>(cx) {
(redirect_fn.f)(&path);
}
// redirect on the client
let navigate = use_navigate(cx);
navigate(&path, options.unwrap_or_default())
}
/// Wrapping type for a function provided as context to allow for
/// server-side redirects. See [provide_server_redirect]
/// and [Redirect].
#[derive(Clone)]
pub struct ServerRedirectFunction {
f: Rc<dyn Fn(&str)>,
}
impl std::fmt::Debug for ServerRedirectFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServerRedirectFunction").finish()
}
}
/// Provides a function that can be used to redirect the user to another
/// absolute path, on the server. This should set a `302` status code and an
/// appropriate `Location` header.
pub fn provide_server_redirect(cx: Scope, handler: impl Fn(&str) + 'static) {
provide_context(
cx,
ServerRedirectFunction {
f: Rc::new(handler),
},
)
}