mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 14:52:35 -05:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7376b76558 | ||
|
|
579abc586c |
@@ -107,7 +107,7 @@ Open browser to [http://localhost:3000/](http://localhost:3000/).
|
||||
|
||||
### What’s up with the name?
|
||||
|
||||
_Leptos_ (λεπτός) is an ancient Greek word meaning “thin, light, refined, fine-grained.” To me, a classicist and not a dog owner, it evokes the lightweight reactive system that powers the framework. I've since learned the same word is at the root of the medical term “leptospirosis,” a blood infection that affects humans and animals... My bad. No dogs were harmed in the creation of this framework.
|
||||
_Leptos_ (λεπτός) is an ancient Greek word meaning “thin, light, refine, fine-grained.” To me, a classicist and not a dog owner, it evokes the lightweight reactive system that powers the framework. I've since learned the same word is at the root of the medical term “leptospirosis,” a blood infection that affects humans and animals... My bad. No dogs were harmed in the creation of this framework.
|
||||
|
||||
### Is it production ready?
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
[tasks.lint]
|
||||
dependencies = ["check-format-flow", "clippy-each-feature"]
|
||||
[tasks.pre-clippy]
|
||||
env = { CARGO_MAKE_CLIPPY_ARGS = "--all-targets --all-features -- -D warnings" }
|
||||
|
||||
[tasks.check-style]
|
||||
dependencies = ["check-format-flow", "clippy-flow"]
|
||||
|
||||
[tasks.check-format]
|
||||
env = { LEPTOS_PROJECT_DIRECTORY = "../" }
|
||||
args = ["fmt", "--", "--check", "--config-path", "${LEPTOS_PROJECT_DIRECTORY}"]
|
||||
|
||||
[tasks.clippy-each-feature]
|
||||
dependencies = ["install-clippy"]
|
||||
command = "cargo"
|
||||
args = ["hack", "clippy", "--all", "--each-feature", "--no-dev-deps"]
|
||||
|
||||
@@ -13,3 +13,6 @@ RUSTFLAGS = "-D warnings"
|
||||
|
||||
[tasks.ci]
|
||||
dependencies = ["lint", "test"]
|
||||
|
||||
[tasks.lint]
|
||||
dependencies = ["check-format-flow"]
|
||||
|
||||
@@ -136,7 +136,7 @@ view! { cx,
|
||||
|
||||
In this example, clicking the button will cause the text inside `<p>` to be updated, cloning `state.name` again! Because signals are the atomic unit of reactivity, updating any field of the signal triggers updates to everything that depends on the signal.
|
||||
|
||||
There’s a better way. You can take fine-grained, reactive slices by using [`create_memo`](https://docs.rs/leptos/latest/leptos/fn.create_memo.html) or [`create_slice`](https://docs.rs/leptos/latest/leptos/fn.create_slice.html) (which uses `create_memo` but also provides a setter). “Memoizing” a value means creating a new reactive value which will only update when it changes. “Memoizing a slice” means creating a new reactive value which will only update when some field of the state struct updates.
|
||||
There’s a better way. You can use take fine-grained, reactive slices by using [`create_memo`](https://docs.rs/leptos/latest/leptos/fn.create_memo.html) or [`create_slice`](https://docs.rs/leptos/latest/leptos/fn.create_slice.html) (which uses `create_memo` but also provides a setter). “Memoizing” a value means creating a new reactive value which will only update when it changes. “Memoizing a slice” means creating a new reactive value which will only update when some field of the state struct updates.
|
||||
|
||||
Here, instead of reading from the state signal directly, we create “slices” of that state with fine-grained updates via `create_slice`. Each slice signal only updates when the particular piece of the larger struct it accesses updates. This means you can create a single root signal, and then take independent, fine-grained slices of it in different components, each of which can update without notifying the others of changes.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use leptos_router::*;
|
||||
|
||||
Routing behavior is provided by the [`<Router/>`](https://docs.rs/leptos_router/latest/leptos_router/fn.Router.html) component. This should usually be somewhere near the root of your application, the rest of the app.
|
||||
|
||||
> You shouldn’t try to use multiple `<Router/>`s in your app. Remember that the router drives global state: if you have multiple routers, which one decides what to do when the URL changes?
|
||||
> You shouldn’t try to use multiple `<Router/>`s in your app. Remember that the router drives global state: if you have multiple routers, which ones decides what to do when the URL changes?
|
||||
|
||||
Let’s start with a simple `<App/>` component using the router:
|
||||
|
||||
@@ -87,17 +87,15 @@ The `view` is a function that takes a `Scope` and returns a view.
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/" view=Home/>
|
||||
<Route path="/users" view=Users/>
|
||||
<Route path="/users/:id" view=UserProfile/>
|
||||
<Route path="/*any" view=NotFound/>
|
||||
<Route path="/" view=|cx| view! { cx, <Home/> }/>
|
||||
<Route path="/users" view=|cx| view! { cx, <Users/> }/>
|
||||
<Route path="/users/:id" view=|cx| view! { cx, <UserProfile/> }/>
|
||||
<Route path="/*any" view=|cx| view! { cx, <NotFound/> }/>
|
||||
</Routes>
|
||||
```
|
||||
|
||||
> `view` takes a `Fn(Scope) -> impl IntoView`. If a component has no props, it is a function that takes `Scope` and returns `impl IntoView`, so it can be passed directly into the `view`. In this case, `view=Home` is just a shorthand for `|cx| view! { cx, <Home/> }`.
|
||||
> The router scores each route to see how good a match it is, so you can define your routes in any order.
|
||||
|
||||
Now if you navigate to `/` or to `/users` you’ll get the home page or the `<Users/>`. If you go to `/users/3` or `/blahblah` you’ll get a user profile or your 404 page (`<NotFound/>`). On every navigation, the router determines which `<Route/>` should be matched, and therefore what content should be displayed where the `<Routes/>` component is defined.
|
||||
|
||||
Note that you can define your routes in any order. The router scores each route to see how good a match it is, rather than simply trying to match them top to bottom.
|
||||
|
||||
Simple enough?
|
||||
|
||||
@@ -4,10 +4,10 @@ We just defined the following set of routes:
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/" view=Home
|
||||
<Route path="/users" view=Users
|
||||
<Route path="/users/:id" view=UserProfile
|
||||
<Route path="/*any" view=NotFound
|
||||
<Route path="/" view=|cx| view! { cx, <Home /> }/>
|
||||
<Route path="/users" view=|cx| view! { cx, <Users /> }/>
|
||||
<Route path="/users/:id" view=|cx| view! { cx, <UserProfile /> }/>
|
||||
<Route path="/*any" view=|cx| view! { cx, <NotFound /> }/>
|
||||
</Routes>
|
||||
```
|
||||
|
||||
@@ -17,11 +17,11 @@ Well... you can!
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/" view=Home
|
||||
<Route path="/users" view=Users
|
||||
<Route path=":id" view=UserProfile
|
||||
<Route path="/" view=|cx| view! { cx, <Home /> }/>
|
||||
<Route path="/users" view=|cx| view! { cx, <Users /> }>
|
||||
<Route path=":id" view=|cx| view! { cx, <UserProfile /> }/>
|
||||
</Route>
|
||||
<Route path="/*any" view=NotFound
|
||||
<Route path="/*any" view=|cx| view! { cx, <NotFound /> }/>
|
||||
</Routes>
|
||||
```
|
||||
|
||||
@@ -39,8 +39,8 @@ Let’s look back at our practical example.
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/users" view=Users
|
||||
<Route path="/users/:id" view=UserProfile
|
||||
<Route path="/users" view=|cx| view! { cx, <Users /> }/>
|
||||
<Route path="/users/:id" view=|cx| view! { cx, <UserProfile /> }/>
|
||||
</Routes>
|
||||
```
|
||||
|
||||
@@ -53,8 +53,8 @@ Let’s say I use nested routes instead:
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/users" view=Users
|
||||
<Route path=":id" view=UserProfile
|
||||
<Route path="/users" view=|cx| view! { cx, <Users /> }>
|
||||
<Route path=":id" view=|cx| view! { cx, <UserProfile /> }/>
|
||||
</Route>
|
||||
</Routes>
|
||||
```
|
||||
@@ -68,9 +68,9 @@ I actually need to add a fallback route
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/users" view=Users
|
||||
<Route path=":id" view=UserProfile
|
||||
<Route path="" view=NoUser
|
||||
<Route path="/users" view=|cx| view! { cx, <Users /> }>
|
||||
<Route path=":id" view=|cx| view! { cx, <UserProfile /> }/>
|
||||
<Route path="" view=|cx| view! { cx, <NoUser /> }/>
|
||||
</Route>
|
||||
</Routes>
|
||||
```
|
||||
@@ -94,8 +94,8 @@ You can easily define this with nested routes
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/contacts" view=ContactList
|
||||
<Route path=":id" view=ContactInfo
|
||||
<Route path="/contacts" view=|cx| view! { cx, <ContactList/> }>
|
||||
<Route path=":id" view=|cx| view! { cx, <ContactInfo/> }/>
|
||||
<Route path="" view=|cx| view! { cx,
|
||||
<p>"Select a contact to view more info."</p>
|
||||
}/>
|
||||
@@ -107,11 +107,11 @@ You can go even deeper. Say you want to have tabs for each contact’s address,
|
||||
|
||||
```rust
|
||||
<Routes>
|
||||
<Route path="/contacts" view=ContactList
|
||||
<Route path=":id" view=ContactInfo
|
||||
<Route path="" view=EmailAndPhone
|
||||
<Route path="address" view=Address
|
||||
<Route path="messages" view=Messages
|
||||
<Route path="/contacts" view=|cx| view! { cx, <ContactList/> }>
|
||||
<Route path=":id" view=|cx| view! { cx, <ContactInfo/> }>
|
||||
<Route path="" view=|cx| view! { cx, <EmailAndPhone/> }/>
|
||||
<Route path="address" view=|cx| view! { cx, <Address/> }/>
|
||||
<Route path="messages" view=|cx| view! { cx, <Messages/> }/>
|
||||
</Route>
|
||||
<Route path="" view=|cx| view! { cx,
|
||||
<p>"Select a contact to view more info."</p>
|
||||
@@ -201,9 +201,12 @@ fn App(cx: Scope) -> impl IntoView {
|
||||
// /contacts has nested routes
|
||||
<Route
|
||||
path="/contacts"
|
||||
view=ContactList
|
||||
view=|cx| view! { cx, <ContactList/> }
|
||||
>
|
||||
// if no id specified, fall back
|
||||
<Route path=":id" view=ContactInfo
|
||||
<Route path=":id" view=|cx| view! { cx,
|
||||
<ContactInfo/>
|
||||
}>
|
||||
<Route path="" view=|cx| view! { cx,
|
||||
<div class="tab">
|
||||
"(Contact Info)"
|
||||
|
||||
@@ -108,9 +108,12 @@ fn App(cx: Scope) -> impl IntoView {
|
||||
// /contacts has nested routes
|
||||
<Route
|
||||
path="/contacts"
|
||||
view=ContactList
|
||||
view=|cx| view! { cx, <ContactList/> }
|
||||
>
|
||||
// if no id specified, fall back
|
||||
<Route path=":id" view=ContactInfo
|
||||
<Route path=":id" view=|cx| view! { cx,
|
||||
<ContactInfo/>
|
||||
}>
|
||||
<Route path="" view=|cx| view! { cx,
|
||||
<div class="tab">
|
||||
"(Contact Info)"
|
||||
|
||||
@@ -52,9 +52,12 @@ fn App(cx: Scope) -> impl IntoView {
|
||||
// /contacts has nested routes
|
||||
<Route
|
||||
path="/contacts"
|
||||
view=ContactList
|
||||
view=|cx| view! { cx, <ContactList/> }
|
||||
>
|
||||
// if no id specified, fall back
|
||||
<Route path=":id" view=ContactInfo
|
||||
<Route path=":id" view=|cx| view! { cx,
|
||||
<ContactInfo/>
|
||||
}>
|
||||
<Route path="" view=|cx| view! { cx,
|
||||
<div class="tab">
|
||||
"(Contact Info)"
|
||||
|
||||
@@ -80,7 +80,7 @@ fn App(cx: Scope) -> impl IntoView {
|
||||
<h1><code>"<Form/>"</code></h1>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=FormExample
|
||||
<Route path="" view=|cx| view! { cx, <FormExample/> }/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
|
||||
@@ -64,7 +64,7 @@ If you’re using server-side rendering, the synchronous mode is almost never wh
|
||||
|
||||
5. **Partially-blocked streaming**: “Partially-blocked” streaming is useful when you have multiple separate `<Suspense/>` components on the page. If one of them reads from one or more “blocking resources” (see below), the fallback will not be sent; rather, the server will wait until that `<Suspense/>` has resolved and then replace the fallback with the resolved fragment on the server, which means that it is included in the initial HTML response and appears even if JavaScript is disabled or not supported. Other `<Suspense/>` stream in out of order as usual.
|
||||
|
||||
This is useful when you have multiple `<Suspense/>` on the page, and one is more important than the other: think of a blog post and comments, or product information and reviews. It is _not_ useful if there’s only one `<Suspense/>`, or if every `<Suspense/>` reads from blocking resources. In those cases it is a slower form of `async` rendering.
|
||||
This is useful when you have multiple `<Suspense/>` on the page, and one is more important than the other: think of a blog post and comments, or product information and reviews. It is *not* useful if there’s only one `<Suspense/>`, or if every `<Suspense/>` reads from blocking resources. In those cases it is a slower form of `async` rendering.
|
||||
|
||||
- _Pros_: Works if JavaScript is disabled or not supported on the user’s device.
|
||||
- _Cons_
|
||||
@@ -79,13 +79,13 @@ Because it offers the best blend of performance characteristics, Leptos defaults
|
||||
```rust
|
||||
<Routes>
|
||||
// We’ll load the home page with out-of-order streaming and <Suspense/>
|
||||
<Route path="" view=HomePage
|
||||
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
|
||||
|
||||
// We'll load the posts with async rendering, so they can set
|
||||
// the title and metadata *after* loading the data
|
||||
<Route
|
||||
path="/post/:id"
|
||||
view=Post
|
||||
view=|cx| view! { cx, <Post/> }
|
||||
ssr=SsrMode::Async
|
||||
/>
|
||||
</Routes>
|
||||
|
||||
@@ -34,7 +34,7 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
</header>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=ExampleErrors/>
|
||||
<Route path="" view=|cx| view! { cx, <ExampleErrors/> }/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
|
||||
@@ -170,7 +170,7 @@ pub fn TodoApp(cx: Scope) -> impl IntoView {
|
||||
<hr/>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=Todos/> //Route
|
||||
<Route path="" view=|cx| view! { cx, <Todos/> }/> //Route
|
||||
<Route path="signup" view=move |cx| view! {
|
||||
cx,
|
||||
<Signup action=signup/>
|
||||
|
||||
@@ -104,7 +104,7 @@ pub fn TodoApp(cx: Scope) -> impl IntoView {
|
||||
</header>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=Todos/>
|
||||
<Route path="" view=|cx| view! { cx, <Todos/> }/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
@@ -126,6 +126,10 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
||||
|
||||
view! {
|
||||
cx,
|
||||
<form method="POST" action="/weird">
|
||||
<input type="text" name="hi" value="John"/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
<div>
|
||||
<MultiActionForm action=add_todo>
|
||||
<label>
|
||||
|
||||
@@ -104,7 +104,10 @@ pub fn TodoApp(cx: Scope) -> impl IntoView {
|
||||
</header>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=Todos/> //Route
|
||||
<Route path="" view=|cx| view! {
|
||||
cx,
|
||||
<Todos/>
|
||||
}/> //Route
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
|
||||
@@ -605,6 +605,8 @@ where
|
||||
let res_options3 = default_res_options.clone();
|
||||
let local_pool = get_leptos_pool();
|
||||
let (tx, rx) = futures::channel::mpsc::channel(8);
|
||||
let (complete_tx, complete_rx) =
|
||||
futures::channel::oneshot::channel();
|
||||
|
||||
let current_span = tracing::Span::current();
|
||||
local_pool.spawn_pinned(move || async move {
|
||||
@@ -631,10 +633,14 @@ where
|
||||
|
||||
forward_stream(&options, res_options2, bundle, runtime, scope, tx).await;
|
||||
|
||||
complete_rx.await.expect("could not receive completion message");
|
||||
eprintln!("\n\n\n\nreceived completion message");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
runtime.dispose();
|
||||
}.instrument(current_span));
|
||||
|
||||
generate_response(res_options3, rx)
|
||||
generate_response(res_options3, rx, complete_tx)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -643,6 +649,7 @@ where
|
||||
async fn generate_response(
|
||||
res_options: ResponseOptions,
|
||||
rx: Receiver<String>,
|
||||
complete_tx: futures::channel::oneshot::Sender<()>,
|
||||
) -> Response<StreamBody<PinnedHtmlStream>> {
|
||||
let mut stream = Box::pin(rx.map(|html| Ok(Bytes::from(html))));
|
||||
|
||||
@@ -655,7 +662,13 @@ async fn generate_response(
|
||||
|
||||
let complete_stream =
|
||||
futures::stream::iter([first_chunk.unwrap(), second_chunk.unwrap()])
|
||||
.chain(stream);
|
||||
.chain(stream)
|
||||
.chain(futures::stream::once(async move {
|
||||
complete_tx
|
||||
.send(())
|
||||
.expect("could not send completion message");
|
||||
Ok(Bytes::from(String::new()))
|
||||
}));
|
||||
|
||||
let mut res = Response::new(StreamBody::new(
|
||||
Box::pin(complete_stream) as PinnedHtmlStream
|
||||
@@ -770,6 +783,8 @@ where
|
||||
let full_path = format!("http://leptos.dev{path}");
|
||||
|
||||
let (tx, rx) = futures::channel::mpsc::channel(8);
|
||||
let (complete_tx, complete_rx) =
|
||||
futures::channel::oneshot::channel();
|
||||
let local_pool = get_leptos_pool();
|
||||
let current_span = tracing::Span::current();
|
||||
local_pool.spawn_pinned(|| async move {
|
||||
@@ -791,10 +806,14 @@ where
|
||||
|
||||
forward_stream(&options, res_options2, bundle, runtime, scope, tx).await;
|
||||
|
||||
complete_rx.await.expect("could not receive completion message");
|
||||
|
||||
eprintln!("\n\n\nreceived completion message");
|
||||
|
||||
runtime.dispose();
|
||||
}.instrument(current_span));
|
||||
|
||||
generate_response(res_options3, rx).await
|
||||
generate_response(res_options3, rx, complete_tx).await
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ pub fn html_parts(
|
||||
let pkg_path = &options.site_pkg_dir;
|
||||
let output_name = &options.output_name;
|
||||
|
||||
// Because wasm-pack adds _bg to the end of the WASM filename, and we want to maintain compatibility with it's default options
|
||||
// Because wasm-pack adds _bg to the end of the WASM filename, and we want to mantain compatibility with it's default options
|
||||
// we add _bg to the wasm files if cargo-leptos doesn't set the env var LEPTOS_OUTPUT_NAME at compile time
|
||||
// Otherwise we need to add _bg because wasm_pack always does.
|
||||
let mut wasm_output_name = output_name.clone();
|
||||
@@ -60,7 +60,7 @@ pub fn html_parts(
|
||||
wasm_output_name.push_str("_bg");
|
||||
}
|
||||
|
||||
let leptos_autoreload = autoreload("", options);
|
||||
let leptos_autoreload = autoreload("".into(), options);
|
||||
|
||||
let html_metadata =
|
||||
meta.and_then(|mc| mc.html.as_string()).unwrap_or_default();
|
||||
@@ -94,7 +94,7 @@ pub fn html_parts_separated(
|
||||
.map(|nonce| format!(" nonce=\"{nonce}\""))
|
||||
.unwrap_or_default();
|
||||
|
||||
// Because wasm-pack adds _bg to the end of the WASM filename, and we want to maintain compatibility with it's default options
|
||||
// Because wasm-pack adds _bg to the end of the WASM filename, and we want to mantain compatibility with it's default options
|
||||
// we add _bg to the wasm files if cargo-leptos doesn't set the env var LEPTOS_OUTPUT_NAME at compile time
|
||||
// Otherwise we need to add _bg because wasm_pack always does.
|
||||
let mut wasm_output_name = output_name.clone();
|
||||
|
||||
@@ -52,12 +52,12 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
<Outlet/>
|
||||
}
|
||||
>
|
||||
<Route path="" view=Nested
|
||||
<Route path="inside" view=NestedResourceInside
|
||||
<Route path="single" view=Single
|
||||
<Route path="parallel" view=Parallel
|
||||
<Route path="inside-component" view=InsideComponent
|
||||
<Route path="none" view=None
|
||||
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||
<Route path="inside" view=|cx| view! { cx, <NestedResourceInside/> }/>
|
||||
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||
<Route path="none" view=|cx| view! { cx, <None/> }/>
|
||||
</Route>
|
||||
// in-order
|
||||
<Route
|
||||
@@ -69,12 +69,12 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
<Outlet/>
|
||||
}
|
||||
>
|
||||
<Route path="" view=Nested
|
||||
<Route path="inside" view=NestedResourceInside
|
||||
<Route path="single" view=Single
|
||||
<Route path="parallel" view=Parallel
|
||||
<Route path="inside-component" view=InsideComponent
|
||||
<Route path="none" view=None
|
||||
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||
<Route path="inside" view=|cx| view! { cx, <NestedResourceInside/> }/>
|
||||
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||
<Route path="none" view=|cx| view! { cx, <None/> }/>
|
||||
</Route>
|
||||
// async
|
||||
<Route
|
||||
@@ -86,12 +86,12 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
<Outlet/>
|
||||
}
|
||||
>
|
||||
<Route path="" view=Nested
|
||||
<Route path="inside" view=NestedResourceInside
|
||||
<Route path="single" view=Single
|
||||
<Route path="parallel" view=Parallel
|
||||
<Route path="inside-component" view=InsideComponent
|
||||
<Route path="none" view=None
|
||||
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||
<Route path="inside" view=|cx| view! { cx, <NestedResourceInside/> }/>
|
||||
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||
<Route path="none" view=|cx| view! { cx, <None/> }/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
@@ -179,9 +179,9 @@ impl TryFrom<String> for Env {
|
||||
/// Loads [LeptosOptions] from a Cargo.toml text content with layered overrides.
|
||||
/// If an env var is specified, like `LEPTOS_ENV`, it will override a setting in the file.
|
||||
pub fn get_config_from_str(text: &str) -> Result<ConfFile, LeptosConfigError> {
|
||||
let re: Regex = Regex::new(r"(?m)^\[package.metadata.leptos\]").unwrap();
|
||||
let re: Regex = Regex::new(r#"(?m)^\[package.metadata.leptos\]"#).unwrap();
|
||||
let re_workspace: Regex =
|
||||
Regex::new(r"(?m)^\[\[workspace.metadata.leptos\]\]").unwrap();
|
||||
Regex::new(r#"(?m)^\[\[workspace.metadata.leptos\]\]"#).unwrap();
|
||||
|
||||
let metadata_name;
|
||||
let start;
|
||||
|
||||
@@ -196,7 +196,7 @@ impl TimeoutHandle {
|
||||
/// Executes the given function after the given duration of time has passed.
|
||||
/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all, fields(duration = ?duration))
|
||||
)]
|
||||
pub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {
|
||||
@@ -206,7 +206,7 @@ pub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {
|
||||
/// Executes the given function after the given duration of time has passed, returning a cancelable handle.
|
||||
/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all, fields(duration = ?duration))
|
||||
)]
|
||||
#[inline(always)]
|
||||
@@ -325,10 +325,11 @@ impl IntervalHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Repeatedly calls the given function, with a delay of the given duration between calls.
|
||||
/// Repeatedly calls the given function, with a delay of the given duration between calls,
|
||||
/// returning a cancelable handle.
|
||||
/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all, fields(duration = ?duration))
|
||||
)]
|
||||
pub fn set_interval(cb: impl Fn() + 'static, duration: Duration) {
|
||||
@@ -339,7 +340,7 @@ pub fn set_interval(cb: impl Fn() + 'static, duration: Duration) {
|
||||
/// returning a cancelable handle.
|
||||
/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all, fields(duration = ?duration))
|
||||
)]
|
||||
#[inline(always)]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::incorrect_clone_impl_on_copy_type)]
|
||||
#![deny(missing_docs)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_traits))]
|
||||
|
||||
@@ -189,6 +189,7 @@ pub fn render_to_stream_with_prefix_undisposed_with_context_and_block_replacemen
|
||||
|
||||
// create the runtime
|
||||
let runtime = create_runtime();
|
||||
eprintln!("\n\ncreated runtime {runtime:?}");
|
||||
|
||||
let ((shell, pending_resources, pending_fragments, serializers), scope, _) =
|
||||
run_scope_undisposed(runtime, {
|
||||
|
||||
@@ -351,10 +351,7 @@ fn root_element_to_tokens_ssr(
|
||||
Ident::new("Custom", Span::call_site())
|
||||
} else {
|
||||
let camel_cased = camel_case_tag_name(
|
||||
tag_name
|
||||
.trim_start_matches("svg::")
|
||||
.trim_start_matches("math::")
|
||||
.trim_end_matches('_'),
|
||||
&tag_name.replace("svg::", "").replace("math::", ""),
|
||||
);
|
||||
Ident::new(&camel_cased, Span::call_site())
|
||||
};
|
||||
@@ -520,17 +517,6 @@ fn element_to_tokens_ssr(
|
||||
&value.replace('{', "\\{").replace('}', "\\}"),
|
||||
);
|
||||
}
|
||||
Node::RawText(r) => {
|
||||
let value = r.to_string_best();
|
||||
let value = if is_script_or_style {
|
||||
value.into()
|
||||
} else {
|
||||
html_escape::encode_safe(&value)
|
||||
};
|
||||
template.push_str(
|
||||
&value.replace('{', "\\{").replace('}', "\\}"),
|
||||
);
|
||||
}
|
||||
Node::Block(NodeBlock::ValidBlock(block)) => {
|
||||
if let Some(value) =
|
||||
block_to_primitive_expression(block)
|
||||
@@ -565,7 +551,7 @@ fn element_to_tokens_ssr(
|
||||
}
|
||||
|
||||
template.push_str("</");
|
||||
template.push_str(tag_name);
|
||||
template.push_str(&node.name().to_string());
|
||||
template.push('>');
|
||||
}
|
||||
}
|
||||
@@ -1855,7 +1841,11 @@ fn is_self_closing(node: &NodeElement) -> bool {
|
||||
fn camel_case_tag_name(tag_name: &str) -> String {
|
||||
let mut chars = tag_name.chars();
|
||||
let first = chars.next();
|
||||
let underscore = if tag_name == "option" { "_" } else { "" };
|
||||
let underscore = if tag_name == "option" || tag_name == "use" {
|
||||
"_"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
first
|
||||
.map(|f| f.to_ascii_uppercase())
|
||||
.into_iter()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::incorrect_clone_impl_on_copy_type)]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(unboxed_closures))]
|
||||
|
||||
@@ -57,15 +57,15 @@ use std::{any::Any, cell::RefCell, fmt, marker::PhantomData, rc::Rc};
|
||||
/// });
|
||||
///
|
||||
/// // instead, we create a memo
|
||||
/// // ✅ creation: the computation does not run on creation, because memos are lazy
|
||||
/// // 🆗 run #1: the calculation runs once immediately
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value.get()));
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 run #1: reading the memo for the first time causes the computation to run for the first time
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// // can be `memoized()` on nightly
|
||||
/// log::debug!("memoized = {}", memoized.get());
|
||||
/// });
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // ✅ reads the current value again **without re-running the calculation**
|
||||
/// // ✅ reads the current value **without re-running the calculation**
|
||||
/// let value = memoized.get();
|
||||
/// // do something else...
|
||||
/// });
|
||||
@@ -149,15 +149,15 @@ where
|
||||
/// });
|
||||
///
|
||||
/// // instead, we create a memo
|
||||
// // ✅ creation: the computation does not run on creation, because memos are lazy
|
||||
/// // 🆗 run #1: the calculation runs once immediately
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value.get()));
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 run #1: reading the memo for the first time causes the computation to run for the first time
|
||||
/// // can be `memoized()` on nightly
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// log::debug!("memoized = {}", memoized.get());
|
||||
/// });
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // ✅ reads the current value again **without re-running the calculation**
|
||||
/// // ✅ reads the current value **without re-running the calculation**
|
||||
/// // can be `memoized()` on nightly
|
||||
/// let value = memoized.get();
|
||||
/// // do something else...
|
||||
/// });
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::{
|
||||
serialization::Serializable,
|
||||
spawn::spawn_local,
|
||||
use_context, GlobalSuspenseContext, Memo, ReadSignal, Scope, ScopeProperty,
|
||||
SignalDispose, SignalGetUntracked, SignalSet, SignalUpdate, SignalWith,
|
||||
SuspenseContext, WriteSignal,
|
||||
SignalGetUntracked, SignalSet, SignalUpdate, SignalWith, SuspenseContext,
|
||||
WriteSignal,
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
@@ -237,7 +237,7 @@ where
|
||||
id,
|
||||
source_ty: PhantomData,
|
||||
out_ty: PhantomData,
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
#[cfg(any(debug_assertions, features = "ssr"))]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
}
|
||||
@@ -373,7 +373,7 @@ where
|
||||
id,
|
||||
source_ty: PhantomData,
|
||||
out_ty: PhantomData,
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
#[cfg(any(debug_assertions, features = "ssr"))]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
}
|
||||
@@ -719,7 +719,7 @@ where
|
||||
pub(crate) id: ResourceId,
|
||||
pub(crate) source_ty: PhantomData<S>,
|
||||
pub(crate) out_ty: PhantomData<T>,
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
#[cfg(any(debug_assertions, features = "ssr"))]
|
||||
pub(crate) defined_at: &'static std::panic::Location<'static>,
|
||||
}
|
||||
|
||||
@@ -744,7 +744,7 @@ where
|
||||
id: self.id,
|
||||
source_ty: PhantomData,
|
||||
out_ty: PhantomData,
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
#[cfg(any(debug_assertions, features = "ssr"))]
|
||||
defined_at: self.defined_at,
|
||||
}
|
||||
}
|
||||
@@ -1093,25 +1093,3 @@ thread_local! {
|
||||
pub fn suppress_resource_load(suppress: bool) {
|
||||
SUPPRESS_RESOURCE_LOAD.with(|w| w.set(suppress));
|
||||
}
|
||||
|
||||
impl<S, T> SignalDispose for Resource<S, T>
|
||||
where
|
||||
S: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
#[track_caller]
|
||||
fn dispose(self) {
|
||||
let res = with_runtime(self.runtime, |runtime| {
|
||||
let mut resources = runtime.resources.borrow_mut();
|
||||
resources.remove(self.id)
|
||||
});
|
||||
if res.ok().flatten().is_none() {
|
||||
crate::macros::debug_warn!(
|
||||
"At {}, you are calling Resource::dispose() on a resource \
|
||||
that no longer exists, probably because its Scope has \
|
||||
already been disposed.",
|
||||
std::panic::Location::caller()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +417,8 @@ impl RuntimeId {
|
||||
pub fn dispose(self) {
|
||||
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
|
||||
{
|
||||
eprintln!("\n\ndisposing of {self:?}");
|
||||
|
||||
let runtime = RUNTIMES.with(move |runtimes| runtimes.borrow_mut().remove(self))
|
||||
.expect("Attempted to dispose of a reactive runtime that was not found. This suggests \
|
||||
a possible memory leak. Please open an issue with details at https://github.com/leptos-rs/leptos");
|
||||
@@ -476,7 +478,7 @@ impl RuntimeId {
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[inline(always)]
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn create_scope(
|
||||
///
|
||||
/// You usually don't need to call this manually.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn raw_scope_and_disposer(runtime: RuntimeId) -> (Scope, ScopeDisposer) {
|
||||
@@ -52,7 +52,7 @@ pub fn raw_scope_and_disposer(runtime: RuntimeId) -> (Scope, ScopeDisposer) {
|
||||
///
|
||||
/// You usually don't need to call this manually.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn run_scope<T>(
|
||||
@@ -69,7 +69,7 @@ pub fn run_scope<T>(
|
||||
///
|
||||
/// You usually don't need to call this manually.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn run_scope_undisposed<T>(
|
||||
@@ -128,7 +128,7 @@ impl Scope {
|
||||
/// dispose of them when they are no longer needed (e.g., a list item has been destroyed or the user
|
||||
/// has navigated away from the route.)
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[inline(always)]
|
||||
@@ -147,7 +147,7 @@ impl Scope {
|
||||
/// dispose of them when they are no longer needed (e.g., a list item has been destroyed or the user
|
||||
/// has navigated away from the route.)
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[inline(always)]
|
||||
@@ -203,7 +203,7 @@ impl Scope {
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[inline(always)]
|
||||
@@ -229,7 +229,7 @@ impl Scope {
|
||||
/// 2. run all cleanup functions defined for this scope by [`on_cleanup`](crate::on_cleanup).
|
||||
/// 3. dispose of all signals, effects, and resources owned by this `Scope`.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn dispose(self) {
|
||||
@@ -306,7 +306,7 @@ impl Scope {
|
||||
})
|
||||
}
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub(crate) fn push_scope_property(&self, prop: ScopeProperty) {
|
||||
@@ -322,7 +322,7 @@ impl Scope {
|
||||
})
|
||||
}
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub(crate) fn remove_scope_property(&self, prop: ScopeProperty) {
|
||||
@@ -343,7 +343,7 @@ impl Scope {
|
||||
})
|
||||
}
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
/// Returns the the parent Scope, if any.
|
||||
@@ -361,7 +361,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
fn push_cleanup(cx: Scope, cleanup_fn: Box<dyn FnOnce()>) {
|
||||
@@ -424,7 +424,7 @@ impl ScopeDisposer {
|
||||
impl Scope {
|
||||
/// Returns IDs for all [`Resource`](crate::Resource)s found on any scope.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn all_resources(&self) -> Vec<ResourceId> {
|
||||
@@ -435,7 +435,7 @@ impl Scope {
|
||||
/// Returns IDs for all [`Resource`](crate::Resource)s found on any scope that are
|
||||
/// pending from the server.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn pending_resources(&self) -> Vec<ResourceId> {
|
||||
@@ -445,7 +445,7 @@ impl Scope {
|
||||
|
||||
/// Returns IDs for all [`Resource`](crate::Resource)s found on any scope.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn serialization_resolvers(
|
||||
@@ -460,7 +460,7 @@ impl Scope {
|
||||
/// Registers the given [`SuspenseContext`](crate::SuspenseContext) with the current scope,
|
||||
/// calling the `resolver` when its resources are all resolved.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn register_suspense(
|
||||
@@ -517,7 +517,7 @@ impl Scope {
|
||||
/// The keys are hydration IDs. Values are tuples of two pinned
|
||||
/// `Future`s that return content for out-of-order and in-order streaming, respectively.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn pending_fragments(&self) -> HashMap<String, FragmentData> {
|
||||
@@ -530,7 +530,7 @@ impl Scope {
|
||||
|
||||
/// A future that will resolve when all blocking fragments are ready.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn blocking_fragments_ready(self) -> PinnedFuture<()> {
|
||||
@@ -557,7 +557,7 @@ impl Scope {
|
||||
/// Returns a tuple of two pinned `Future`s that return content for out-of-order
|
||||
/// and in-order streaming, respectively.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn take_pending_fragment(&self, id: &str) -> Option<FragmentData> {
|
||||
@@ -576,7 +576,7 @@ impl Scope {
|
||||
/// # Panics
|
||||
/// Panics if the runtime this scope belongs to has already been disposed.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[inline(always)]
|
||||
|
||||
@@ -95,7 +95,7 @@ cfg_if! {
|
||||
|
||||
fn de(json: &str) -> Result<Self, SerializationError> {
|
||||
let intermediate =
|
||||
serde_json::from_str(json).map_err(|e| SerializationError::Deserialize(Rc::new(e)))?;
|
||||
serde_json::from_str(&json).map_err(|e| SerializationError::Deserialize(Rc::new(e)))?;
|
||||
Self::deserialize(&intermediate).map_err(|e| SerializationError::Deserialize(Rc::new(e)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ impl<T> StoredValue<T> {
|
||||
/// Same as [`StoredValue::with_value`] but returns [`Some(O)]` only if
|
||||
/// the stored value has not yet been disposed. [`None`] otherwise.
|
||||
pub fn try_with_value<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
|
||||
eprintln!("\n\nlooking in {:?}", self.runtime);
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
let value = {
|
||||
let values = runtime.stored_values.borrow();
|
||||
@@ -293,6 +294,7 @@ pub fn store_value<T>(cx: Scope, value: T) -> StoredValue<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
eprintln!("\nstoring in {:?}", cx.runtime);
|
||||
let id = with_runtime(cx.runtime, |runtime| {
|
||||
runtime
|
||||
.stored_values
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{Scope, ScopeProperty};
|
||||
|
||||
/// A version of [`create_effect`](crate::create_effect) that listens to any dependency that is accessed inside `deps` and returns
|
||||
/// A version of [`create_effect`] that listens to any dependency that is accessed inside `deps` and returns
|
||||
/// a stop handler.
|
||||
/// The return value of `deps` is passed into `callback` as an argument together with the previous value.
|
||||
/// Additionally the last return value of `callback` is provided as a third argument as is done in [`create_effect`](crate::create_effect).
|
||||
/// Additionally the last return value of `callback` is provided as a third argument as is done in [`create_effect`].
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
|
||||
@@ -135,6 +135,7 @@ where
|
||||
/// The URL associated with the action (typically as part of a server function.)
|
||||
/// This enables integration with the `ActionForm` component in `leptos_router`.
|
||||
pub fn url(&self) -> Option<String> {
|
||||
eprintln!("action = {:?}", self.0);
|
||||
self.0.with_value(|a| a.url.as_ref().cloned())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::incorrect_clone_impl_on_copy_type)]
|
||||
#![deny(missing_docs)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ where
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn dispatch(&self, input: I) {
|
||||
@@ -104,7 +104,7 @@ where
|
||||
|
||||
/// The set of all submissions to this multi-action.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn submissions(&self) -> ReadSignal<Vec<Submission<I, O>>> {
|
||||
@@ -119,7 +119,7 @@ where
|
||||
|
||||
/// How many times an action has successfully resolved.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn version(&self) -> RwSignal<usize> {
|
||||
@@ -129,7 +129,7 @@ where
|
||||
/// Associates the URL of the given server function with this action.
|
||||
/// This enables integration with the `MultiActionForm` component in `leptos_router`.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn using_server_fn<T: ServerFn>(self) -> Self {
|
||||
@@ -212,7 +212,7 @@ where
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn dispatch(&self, input: I) {
|
||||
@@ -304,7 +304,7 @@ where
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn create_multi_action<I, O, F, Fu>(
|
||||
@@ -351,7 +351,7 @@ where
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
any(debug_assertions, features = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
pub fn create_server_multi_action<S>(
|
||||
|
||||
@@ -56,23 +56,23 @@
|
||||
//! // our root route: the contact list is always shown
|
||||
//! <Route
|
||||
//! path=""
|
||||
//! view=ContactList
|
||||
//! view=move |cx| view! { cx, <ContactList/> }
|
||||
//! >
|
||||
//! // users like /gbj or /bob
|
||||
//! <Route
|
||||
//! path=":id"
|
||||
//! view=Contact
|
||||
//! view=move |cx| view! { cx, <Contact/> }
|
||||
//! />
|
||||
//! // a fallback if the /:id segment is missing from the URL
|
||||
//! <Route
|
||||
//! path=""
|
||||
//! view=move |_| view! { cx, <p class="contact">"Select a contact."</p> }
|
||||
//! view=move |_| view! { cx, <p class="contact">"Select a contact."</p> }
|
||||
//! />
|
||||
//! </Route>
|
||||
//! // LR will automatically use this for /about, not the /:id match above
|
||||
//! <Route
|
||||
//! path="about"
|
||||
//! view=About
|
||||
//! view=move |cx| view! { cx, <About/> }
|
||||
//! />
|
||||
//! </Routes>
|
||||
//! </main>
|
||||
|
||||
@@ -101,5 +101,5 @@ pub fn expand_optionals(pattern: &str) -> Vec<Cow<str>> {
|
||||
}
|
||||
}
|
||||
|
||||
const OPTIONAL: &str = r"(/?:[^/]+)\?";
|
||||
const OPTIONAL_2: &str = r"^(/:[^/]+)\?";
|
||||
const OPTIONAL: &str = r#"(/?:[^/]+)\?"#;
|
||||
const OPTIONAL_2: &str = r#"^(/:[^/]+)\?"#;
|
||||
|
||||
Reference in New Issue
Block a user