diff --git a/Cargo.toml b/Cargo.toml index 2ccc8a84f..23035d3e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,22 +26,22 @@ members = [ exclude = ["benchmarks", "examples"] [workspace.package] -version = "0.4.9" +version = "0.5.0" [workspace.dependencies] -leptos = { path = "./leptos", version = "0.4.9" } -leptos_dom = { path = "./leptos_dom", version = "0.4.9" } -leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.4.9" } -leptos_macro = { path = "./leptos_macro", version = "0.4.9" } -leptos_reactive = { path = "./leptos_reactive", version = "0.4.9" } -leptos_server = { path = "./leptos_server", version = "0.4.9" } -server_fn = { path = "./server_fn", version = "0.4.9" } -server_fn_macro = { path = "./server_fn_macro", version = "0.4.9" } -server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.4.9" } -leptos_config = { path = "./leptos_config", version = "0.4.9" } -leptos_router = { path = "./router", version = "0.4.9" } -leptos_meta = { path = "./meta", version = "0.4.9" } -leptos_integration_utils = { path = "./integrations/utils", version = "0.4.9" } +leptos = { path = "./leptos", version = "0.5.0" } +leptos_dom = { path = "./leptos_dom", version = "0.5.0" } +leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.5.0" } +leptos_macro = { path = "./leptos_macro", version = "0.5.0" } +leptos_reactive = { path = "./leptos_reactive", version = "0.5.0" } +leptos_server = { path = "./leptos_server", version = "0.5.0" } +server_fn = { path = "./server_fn", version = "0.5.0" } +server_fn_macro = { path = "./server_fn_macro", version = "0.5.0" } +server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.5.0" } +leptos_config = { path = "./leptos_config", version = "0.5.0" } +leptos_router = { path = "./router", version = "0.5.0" } +leptos_meta = { path = "./meta", version = "0.5.0" } +leptos_integration_utils = { path = "./integrations/utils", version = "0.5.0" } [profile.release] codegen-units = 1 diff --git a/README.md b/README.md index 4234fb16b..a0258c47e 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ use leptos::*; #[component] -pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView { +pub fn SimpleCounter(initial_value: i32) -> impl IntoView { // create a reactive signal with the initial value - let (value, set_value) = create_signal(cx, initial_value); + let (value, set_value) = create_signal(initial_value); // create event handlers for our buttons // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures @@ -27,19 +27,22 @@ pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView { let increment = move |_| set_value.update(|value| *value += 1); // create user interfaces with the declarative `view!` macro - view! { cx, + view! {
- - + + + // text nodes can be quoted or unquoted "Value: " {value} "!" - +
} } // Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setup pub fn main() { - mount_to_body(|cx| view! { cx, }) + mount_to_body(|| view! { + + }) } ``` @@ -113,7 +116,7 @@ People usually mean one of three things by this question. 1. **Are the APIs stable?** i.e., will I have to rewrite my whole app from Leptos 0.1 to 0.2 to 0.3 to 0.4, or can I write it now and benefit from new features and updates as new versions come? -The APIs are basically settled. We’re adding new features, but we’re very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to future releases. The sorts of breaking changes that we discuss are things like “Oh yeah, that function should probably take `cx` as its argument...” not major changes to the way you write your application. +The APIs are basically settled. We’re adding new features, but we’re very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to future releases, in terms of architecture. 2. **Are there bugs?** @@ -152,13 +155,13 @@ There are some practical differences that make a significant difference: - **Templating:** Leptos uses a JSX-like template format (built on [syn-rsx](https://github.com/stoically/syn-rsx)) for its `view` macro. Sycamore offers the choice of its own templating DSL or a builder syntax. - **Server integration:** Leptos provides primitives that encourage HTML streaming and allow for easy async integration and RPC calls, even without WASM enabled, making it easy to opt into integrations between your frontend and backend code without pushing you toward any particular metaframework patterns. -- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use [`create_rw_signal`](https://docs.rs/leptos/latest/leptos/fn.create_rw_signal.html) to give a unified read/write signal.)_ +- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(0);` _(If you prefer or if it's more convenient for your API, you can use [`create_rw_signal`](https://docs.rs/leptos/latest/leptos/fn.create_rw_signal.html) to give a unified read/write signal.)_ - **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example: ```rust - let (count, set_count) = create_signal(cx, 0); // a signal + let (count, set_count) = create_signal(0); // a signal let double_count = move || count() * 2; // a derived signal - let memoized_count = create_memo(cx, move |_| count() * 3); // a memo + let memoized_count = create_memo(move |_| count() * 3); // a memo // all are accessed by calling them assert_eq!(count(), 0); assert_eq!(double_count(), 0); diff --git a/benchmarks/src/reactive.rs b/benchmarks/src/reactive.rs index e3009621a..d283a2396 100644 --- a/benchmarks/src/reactive.rs +++ b/benchmarks/src/reactive.rs @@ -7,15 +7,15 @@ fn leptos_deep_creation(b: &mut Bencher) { let runtime = create_runtime(); b.iter(|| { - create_scope(runtime, |cx| { - let signal = create_rw_signal(cx, 0); + create_scope(runtime, || { + let signal = create_rw_signal(0); let mut memos = Vec::>::new(); for _ in 0..1000usize { let prev = memos.last().copied(); if let Some(prev) = prev { - memos.push(create_memo(cx, move |_| prev.get() + 1)); + memos.push(create_memo(move |_| prev.get() + 1)); } else { - memos.push(create_memo(cx, move |_| signal.get() + 1)); + memos.push(create_memo(move |_| signal.get() + 1)); } } }) @@ -31,14 +31,14 @@ fn leptos_deep_update(b: &mut Bencher) { let runtime = create_runtime(); b.iter(|| { - create_scope(runtime, |cx| { - let signal = create_rw_signal(cx, 0); + create_scope(runtime, || { + let signal = create_rw_signal(0); let mut memos = Vec::>::new(); for _ in 0..1000usize { if let Some(prev) = memos.last().copied() { - memos.push(create_memo(cx, move |_| prev.get() + 1)); + memos.push(create_memo(move |_| prev.get() + 1)); } else { - memos.push(create_memo(cx, move |_| signal.get() + 1)); + memos.push(create_memo(move |_| signal.get() + 1)); } } signal.set(1); @@ -56,12 +56,11 @@ fn leptos_narrowing_down(b: &mut Bencher) { let runtime = create_runtime(); b.iter(|| { - create_scope(runtime, |cx| { - let sigs = - (0..1000).map(|n| create_signal(cx, n)).collect::>(); + create_scope(runtime, || { + let sigs = (0..1000).map(|n| create_signal(n)).collect::>(); let reads = sigs.iter().map(|(r, _)| *r).collect::>(); let writes = sigs.iter().map(|(_, w)| *w).collect::>(); - let memo = create_memo(cx, move |_| { + let memo = create_memo(move |_| { reads.iter().map(|r| r.get()).sum::() }); assert_eq!(memo(), 499500); @@ -78,10 +77,10 @@ fn leptos_fanning_out(b: &mut Bencher) { let runtime = create_runtime(); b.iter(|| { - create_scope(runtime, |cx| { - let sig = create_rw_signal(cx, 0); + create_scope(runtime, || { + let sig = create_rw_signal(0); let memos = (0..1000) - .map(|_| create_memo(cx, move |_| sig.get())) + .map(|_| create_memo(move |_| sig.get())) .collect::>(); assert_eq!(memos.iter().map(|m| m.get()).sum::(), 0); sig.set(1); @@ -99,17 +98,16 @@ fn leptos_narrowing_update(b: &mut Bencher) { let runtime = create_runtime(); b.iter(|| { - create_scope(runtime, |cx| { + create_scope(runtime, || { let acc = Rc::new(Cell::new(0)); - let sigs = - (0..1000).map(|n| create_signal(cx, n)).collect::>(); + let sigs = (0..1000).map(|n| create_signal(n)).collect::>(); let reads = sigs.iter().map(|(r, _)| *r).collect::>(); let writes = sigs.iter().map(|(_, w)| *w).collect::>(); - let memo = create_memo(cx, move |_| { + let memo = create_memo(move |_| { reads.iter().map(|r| r.get()).sum::() }); assert_eq!(memo(), 499500); - create_isomorphic_effect(cx, { + create_isomorphic_effect({ let acc = Rc::clone(&acc); move |_| { acc.set(memo()); @@ -141,9 +139,9 @@ fn leptos_scope_creation_and_disposal(b: &mut Bencher) { .map(|_| { create_scope(runtime, { let acc = Rc::clone(&acc); - move |cx| { - let (r, w) = create_signal(cx, 0); - create_isomorphic_effect(cx, { + move || { + let (r, w) = create_signal(0); + create_isomorphic_effect({ move |_| { acc.set(r()); } @@ -163,7 +161,9 @@ fn leptos_scope_creation_and_disposal(b: &mut Bencher) { #[bench] fn rs_deep_update(b: &mut Bencher) { - use reactive_signals::{Scope, Signal, signal, runtimes::ClientRuntime, types::Func}; + use reactive_signals::{ + runtimes::ClientRuntime, signal, types::Func, Scope, Signal, + }; let sc = ClientRuntime::new_root_scope(); b.iter(|| { @@ -184,7 +184,9 @@ fn rs_deep_update(b: &mut Bencher) { #[bench] fn rs_fanning_out(b: &mut Bencher) { - use reactive_signals::{Scope, Signal, signal, runtimes::ClientRuntime, types::Func}; + use reactive_signals::{ + runtimes::ClientRuntime, signal, types::Func, Scope, Signal, + }; let cx = ClientRuntime::new_root_scope(); b.iter(|| { @@ -200,18 +202,17 @@ fn rs_fanning_out(b: &mut Bencher) { #[bench] fn rs_narrowing_update(b: &mut Bencher) { - use reactive_signals::{Scope, Signal, signal, runtimes::ClientRuntime, types::Func}; + use reactive_signals::{ + runtimes::ClientRuntime, signal, types::Func, Scope, Signal, + }; let cx = ClientRuntime::new_root_scope(); b.iter(|| { let acc = Rc::new(Cell::new(0)); - let sigs = - (0..1000).map(|n| signal!(cx, n)).collect::>(); + let sigs = (0..1000).map(|n| signal!(cx, n)).collect::>(); let memo = signal!(cx, { let sigs = sigs.clone(); - move || { - sigs.iter().map(|r| r.get()).sum::() - } + move || sigs.iter().map(|r| r.get()).sum::() }); assert_eq!(memo.get(), 499500); signal!(cx, { diff --git a/benchmarks/src/ssr.rs b/benchmarks/src/ssr.rs index ce99ed571..a652b1b94 100644 --- a/benchmarks/src/ssr.rs +++ b/benchmarks/src/ssr.rs @@ -7,7 +7,7 @@ fn leptos_ssr_bench(b: &mut Bencher) { leptos_dom::HydrationCtx::reset_id(); _ = create_scope(create_runtime(), |cx| { #[component] - fn Counter(cx: Scope, initial: i32) -> impl IntoView { + fn Counter(initial: i32) -> impl IntoView { let (value, set_value) = create_signal(cx, initial); view! { cx, diff --git a/benchmarks/src/todomvc/leptos.rs b/benchmarks/src/todomvc/leptos.rs index 3555c85c6..97e36c210 100644 --- a/benchmarks/src/todomvc/leptos.rs +++ b/benchmarks/src/todomvc/leptos.rs @@ -1,7 +1,7 @@ pub use leptos::*; use miniserde::*; -use web_sys::HtmlInputElement; use wasm_bindgen::JsCast; +use web_sys::HtmlInputElement; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Todos(pub Vec); @@ -9,13 +9,13 @@ pub struct Todos(pub Vec); const STORAGE_KEY: &str = "todos-leptos"; impl Todos { - pub fn new(cx: Scope) -> Self { + pub fn new() -> Self { Self(vec![]) } - pub fn new_with_1000(cx: Scope) -> Self { + pub fn new_with_1000() -> Self { let todos = (0..1000) - .map(|id| Todo::new(cx, id, format!("Todo #{id}"))) + .map(|id| Todo::new(id, format!("Todo #{id}"))) .collect(); Self(todos) } @@ -72,13 +72,17 @@ pub struct Todo { } impl Todo { - pub fn new(cx: Scope, id: usize, title: String) -> Self { - Self::new_with_completed(cx, id, title, false) + pub fn new(id: usize, title: String) -> Self { + Self::new_with_completed(id, title, false) } - pub fn new_with_completed(cx: Scope, id: usize, title: String, completed: bool) -> Self { - let (title, set_title) = create_signal(cx, title); - let (completed, set_completed) = create_signal(cx, completed); + pub fn new_with_completed( + id: usize, + title: String, + completed: bool, + ) -> Self { + let (title, set_title) = create_signal(title); + let (completed, set_completed) = create_signal(completed); Self { id, title, @@ -98,7 +102,7 @@ const ESCAPE_KEY: u32 = 27; const ENTER_KEY: u32 = 13; #[component] -pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { +pub fn TodoMVC(todos: Todos) -> impl IntoView { let mut next_id = todos .0 .iter() @@ -107,10 +111,10 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { .map(|last| last + 1) .unwrap_or(0); - let (todos, set_todos) = create_signal(cx, todos); - provide_context(cx, set_todos); + let (todos, set_todos) = create_signal(todos); + provide_context(set_todos); - let (mode, set_mode) = create_signal(cx, Mode::All); + let (mode, set_mode) = create_signal(Mode::All); let add_todo = move |ev: web_sys::KeyboardEvent| { let target = event_target::(&ev); @@ -120,7 +124,7 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { let title = event_target_value(&ev); let title = title.trim(); if !title.is_empty() { - let new = Todo::new(cx, next_id, title.to_string()); + let new = Todo::new(next_id, title.to_string()); set_todos.update(|t| t.add(new)); next_id += 1; target.set_value(""); @@ -128,7 +132,7 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { } }; - let filtered_todos = create_memo::>(cx, move |_| { + let filtered_todos = create_memo::>(move |_| { todos.with(|todos| match mode.get() { Mode::All => todos.0.to_vec(), Mode::Active => todos @@ -148,7 +152,7 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { // effect to serialize to JSON // this does reactive reads, so it will automatically serialize on any relevant change - create_effect(cx, move |_| { + create_effect(move |_| { if let Ok(Some(storage)) = window().local_storage() { let objs = todos .get() @@ -163,7 +167,7 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { } }); - view! { cx, + view! {
@@ -188,8 +192,8 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView { } + view=move |todo: Todo| { + view! { } } /> @@ -236,14 +240,14 @@ pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView {

"Part of " "TodoMVC"

- }.into_view(cx) + }.into_view() } #[component] -pub fn Todo(cx: Scope, todo: Todo) -> impl IntoView { - let (editing, set_editing) = create_signal(cx, false); - let set_todos = use_context::>(cx).unwrap(); - //let input = NodeRef::new(cx); +pub fn Todo(todo: Todo) -> impl IntoView { + let (editing, set_editing) = create_signal(false); + let set_todos = use_context::>().unwrap(); + //let input = NodeRef::new(); let save = move |value: &str| { let value = value.trim(); @@ -255,7 +259,7 @@ pub fn Todo(cx: Scope, todo: Todo) -> impl IntoView { set_editing(false); }; - view! { cx, + view! {
  • @@ -268,7 +272,7 @@ pub fn Todo(cx: Scope, todo: Todo) -> impl IntoView { {move || { editing() .then(|| { - view! { cx, + view! { Todo { - Todo::new_with_completed(cx, self.id, self.title, self.completed) + pub fn into_todo(self, ) -> Todo { + Todo::new_with_completed(self.id, self.title, self.completed) } } diff --git a/benchmarks/src/todomvc/mod.rs b/benchmarks/src/todomvc/mod.rs index c006ee97f..608f9a88b 100644 --- a/benchmarks/src/todomvc/mod.rs +++ b/benchmarks/src/todomvc/mod.rs @@ -12,8 +12,8 @@ fn leptos_todomvc_ssr(b: &mut Bencher) { b.iter(|| { use crate::todomvc::leptos::*; - let html = ::leptos::ssr::render_to_string(|cx| { - view! { cx, } + let html = ::leptos::ssr::render_to_string(|| { + view! { } }); assert!(html.len() > 1); }); diff --git a/docs/COMMON_BUGS.md b/docs/COMMON_BUGS.md index ffc8c75a8..a22661cd0 100644 --- a/docs/COMMON_BUGS.md +++ b/docs/COMMON_BUGS.md @@ -9,10 +9,10 @@ This document is intended as a running list of common issues, with example code **Issue**: Sometimes you want to update a reactive signal in a way that depends on another signal. ```rust -let (a, set_a) = create_signal(cx, 0); -let (b, set_b) = create_signal(cx, false); +let (a, set_a) = create_signal(0); +let (b, set_b) = create_signal(false); -create_effect(cx, move |_| { +create_effect(move |_| { if a() > 5 { set_b(true); } @@ -24,7 +24,7 @@ This creates an inefficient chain of updates, and can easily lead to infinite lo **Solution**: Follow the rule, _What can be derived, should be derived._ In this case, this has the benefit of massively reducing the code size, too! ```rust -let (a, set_a) = create_signal(cx, 0); +let (a, set_a) = create_signal(0); let b = move || a () > 5; ``` @@ -34,19 +34,19 @@ Sometimes you have nested signals: for example, hash-map that can change over ti ```rust #[component] -pub fn App(cx: Scope) -> impl IntoView { - let resources = create_rw_signal(cx, HashMap::new()); +pub fn App() -> impl IntoView { + let resources = create_rw_signal(HashMap::new()); let update = move |id: usize| { resources.update(|resources| { resources .entry(id) - .or_insert_with(|| create_rw_signal(cx, 0)) + .or_insert_with(|| create_rw_signal(0)) .update(|amount| *amount += 1) }) }; - view! { cx, + view! {
    {move || format!("{:#?}", resources.get().into_iter().map(|(id, resource)| (id, resource.get())).collect::>())}
    @@ -55,17 +55,17 @@ pub fn App(cx: Scope) -> impl IntoView { } ``` -Clicking the button twice will cause a panic, because of the nested signal *read*. Calling the `update` function on `resources` immediately takes out a mutable borrow on `resources`, then updates the `resource` signal—which re-runs the effect that reads from the signals, which tries to immutably access `resources` and panics. It's the nested update here which causes a problem, because the inner update triggers and effect that tries to read both signals while the outer is still updating. +Clicking the button twice will cause a panic, because of the nested signal _read_. Calling the `update` function on `resources` immediately takes out a mutable borrow on `resources`, then updates the `resource` signal—which re-runs the effect that reads from the signals, which tries to immutably access `resources` and panics. It's the nested update here which causes a problem, because the inner update triggers and effect that tries to read both signals while the outer is still updating. -You can fix this fairly easily by using the [`Scope::batch()`](https://docs.rs/leptos/latest/leptos/struct.Scope.html#method.batch) method: +You can fix this fairly easily by using the [`batch()`](https://docs.rs/leptos/latest/leptos/fn.batch.html) method: ```rust let update = move |id: usize| { - cx.batch(move || { + batch(move || { resources.update(|resources| { resources .entry(id) - .or_insert_with(|| create_rw_signal(cx, 0)) + .or_insert_with(|| create_rw_signal(0)) .update(|amount| *amount += 1) }) }); @@ -83,11 +83,11 @@ Many DOM attributes can be updated either by setting an attribute on the DOM nod This means that in practice, attributes like `value` or `checked` on an `` element only update the _default_ value for the ``. If you want to reactively update the value, you should use `prop:value` instead to set the `value` property. ```rust -let (a, set_a) = create_signal(cx, "Starting value".to_string()); +let (a, set_a) = create_signal("Starting value".to_string()); let on_input = move |ev| set_a(event_target_value(&ev)); view! { - cx, + // ❌ reactivity doesn't work as expected: typing only updates the default // of each input, so if you start typing in the second input, it won't // update the first one @@ -97,11 +97,11 @@ view! { ``` ```rust -let (a, set_a) = create_signal(cx, "Starting value".to_string()); +let (a, set_a) = create_signal("Starting value".to_string()); let on_input = move |ev| set_a(event_target_value(&ev)); view! { - cx, + // ✅ works as intended by setting the value *property* diff --git a/docs/book/src/02_getting_started.md b/docs/book/src/02_getting_started.md index 34f37fd8c..3958c6467 100644 --- a/docs/book/src/02_getting_started.md +++ b/docs/book/src/02_getting_started.md @@ -27,6 +27,7 @@ cargo add leptos --features=csr,nightly ``` Or you can leave off `nightly` if you're using stable Rust + ```bash cargo add leptos --features=csr ``` @@ -64,7 +65,7 @@ And add a simple “Hello, world!” to your `main.rs` use leptos::*; fn main() { - mount_to_body(|cx| view! { cx,

    "Hello, world!"

    }) + mount_to_body(|| view! {

    "Hello, world!"

    }) } ``` diff --git a/docs/book/src/15_global_state.md b/docs/book/src/15_global_state.md index e17845a21..5fa73f655 100644 --- a/docs/book/src/15_global_state.md +++ b/docs/book/src/15_global_state.md @@ -29,15 +29,15 @@ all its children and descendants using `provide_context`. ```rust #[component] -fn App(cx: Scope) -> impl IntoView { +fn App() -> impl IntoView { // here we create a signal in the root that can be consumed // anywhere in the app. - let (count, set_count) = create_signal(cx, 0); + let (count, set_count) = create_signal(0); // we'll pass the setter to specific components, // but provide the count itself to the whole app via context - provide_context(cx, count); + provide_context(count); - view! { cx, + view! { // SetterButton is allowed to modify the count // These consumers can only read from it @@ -57,14 +57,14 @@ fn App(cx: Scope) -> impl IntoView { ```rust /// A component that does some "fancy" math with the global count #[component] -fn FancyMath(cx: Scope) -> impl IntoView { +fn FancyMath() -> impl IntoView { // here we consume the global count signal with `use_context` - let count = use_context::>(cx) + let count = use_context::>() // we know we just provided this in the parent component .expect("there to be a `count` signal provided"); let is_even = move || count() & 1 == 0; - view! { cx, + view! {
    "The number " {count} @@ -89,17 +89,17 @@ struct GlobalState { } impl GlobalState { - pub fn new(cx: Scope) -> Self { + pub fn new() -> Self { Self { - count: create_rw_signal(cx, 0), - name: create_rw_signal(cx, "Bob".to_string()) + count: create_rw_signal(0), + name: create_rw_signal("Bob".to_string()) } } } #[component] -fn App(cx: Scope) -> impl IntoView { - provide_context(cx, GlobalState::new(cx)); +fn App() -> impl IntoView { + provide_context(GlobalState::new()); // etc. } @@ -117,8 +117,8 @@ struct GlobalState { } #[component] -fn App(cx: Scope) -> impl IntoView { - provide_context(cx, create_rw_signal(GlobalState::default())); +fn App() -> impl IntoView { + provide_context(create_rw_signal(GlobalState::default())); // etc. } @@ -127,8 +127,8 @@ fn App(cx: Scope) -> impl IntoView { But there’s a problem: because our whole state is wrapped in one signal, updating the value of one field will cause reactive updates in parts of the UI that only depend on the other. ```rust -let state = expect_context::>(cx); -view! { cx, +let state = expect_context::>(); +view! {

    {move || state.with(|state| state.name.clone())}

    } @@ -143,12 +143,12 @@ Here, instead of reading from the state signal directly, we create “slices” ```rust /// A component that updates the count in the global state. #[component] -fn GlobalStateCounter(cx: Scope) -> impl IntoView { - let state = expect_context::>(cx); +fn GlobalStateCounter() -> impl IntoView { + let state = expect_context::>(); // `create_slice` lets us create a "lens" into the data let (count, set_count) = create_slice( - cx, + // we take a slice *from* `state` state, // our getter returns a "slice" of the data @@ -157,7 +157,7 @@ fn GlobalStateCounter(cx: Scope) -> impl IntoView { |state, n| state.count = n, ); - view! { cx, + view! {

    - "stable"": " {move || stable.read(cx)} + "stable"": " {move || stable.read()}

    "count"": " {count} @@ -132,7 +129,7 @@ fn App(cx: Scope) -> impl IntoView { } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/async/11_suspense.md b/docs/book/src/async/11_suspense.md index f68388ebe..099d8a034 100644 --- a/docs/book/src/async/11_suspense.md +++ b/docs/book/src/async/11_suspense.md @@ -3,14 +3,14 @@ In the previous chapter, we showed how you can create a simple loading screen to show some fallback while a resource is loading. ```rust -let (count, set_count) = create_signal(cx, 0); -let a = create_resource(cx, count, |count| async move { load_a(count).await }); +let (count, set_count) = create_signal(0); +let a = create_resource(count, |count| async move { load_a(count).await }); -view! { cx, +view! {

    "My Data"

    - {move || match once.read(cx) { - None => view! { cx,

    "Loading..."

    }.into_view(cx), - Some(data) => view! { cx, }.into_view(cx) + {move || match once.read() { + None => view! {

    "Loading..."

    }.into_view(), + Some(data) => view! { }.into_view() }} } ``` @@ -18,19 +18,19 @@ view! { cx, But what if we have two resources, and want to wait for both of them? ```rust -let (count, set_count) = create_signal(cx, 0); -let (count2, set_count2) = create_signal(cx, 0); -let a = create_resource(cx, count, |count| async move { load_a(count).await }); -let b = create_resource(cx, count2, |count| async move { load_b(count).await }); +let (count, set_count) = create_signal(0); +let (count2, set_count2) = create_signal(0); +let a = create_resource(count, |count| async move { load_a(count).await }); +let b = create_resource(count2, |count| async move { load_b(count).await }); -view! { cx, +view! {

    "My Data"

    - {move || match (a.read(cx), b.read(cx)) { - (Some(a), Some(b)) => view! { cx, + {move || match (a.read(), b.read()) { + (Some(a), Some(b)) => view! { - }.into_view(cx), - _ => view! { cx,

    "Loading..."

    }.into_view(cx) + }.into_view(), + _ => view! {

    "Loading..."

    }.into_view() }} } ``` @@ -40,26 +40,26 @@ That’s not _so_ bad, but it’s kind of annoying. What if we could invert the The [``](https://docs.rs/leptos/latest/leptos/fn.Suspense.html) component lets us do exactly that. You give it a `fallback` prop and children, one or more of which usually involves reading from a resource. Reading from a resource “under” a `` (i.e., in one of its children) registers that resource with the ``. If it’s still waiting for resources to load, it shows the `fallback`. When they’ve all loaded, it shows the children. ```rust -let (count, set_count) = create_signal(cx, 0); -let (count2, set_count2) = create_signal(cx, 0); -let a = create_resource(cx, count, |count| async move { load_a(count).await }); -let b = create_resource(cx, count2, |count| async move { load_b(count).await }); +let (count, set_count) = create_signal(0); +let (count2, set_count2) = create_signal(0); +let a = create_resource(count, |count| async move { load_a(count).await }); +let b = create_resource(count2, |count| async move { load_b(count).await }); -view! { cx, +view! {

    "My Data"

    "Loading..."

    } + fallback=move || view! {

    "Loading..."

    } >

    "My Data"

    "A"

    {move || { - a.read(cx) - .map(|a| view! { cx, }) + a.read() + .map(|a| view! { }) }}

    "B"

    {move || { - b.read(cx) - .map(|b| view! { cx, }) + b.read() + .map(|b| view! { }) }}
    } @@ -84,10 +84,10 @@ async fn fetch_monkeys(monkey: i32) -> i32 { // maybe this didn't need to be async monkey * 2 } -view! { cx, +view! { @@ -114,17 +114,17 @@ async fn important_api_call(name: String) -> String { } #[component] -fn App(cx: Scope) -> impl IntoView { - let (name, set_name) = create_signal(cx, "Bill".to_string()); +fn App() -> impl IntoView { + let (name, set_name) = create_signal("Bill".to_string()); // this will reload every time `name` changes let async_data = create_resource( - cx, + name, |name| async move { important_api_call(name).await }, ); - view! { cx, + view! { impl IntoView { "Loading..."

    } + fallback=move || view! {

    "Loading..."

    } > // the children will be rendered once initially, // and then whenever any resources has been resolved

    "Your shouting name is " - {move || async_data.read(cx)} + {move || async_data.read()}

    } } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/async/12_transition.md b/docs/book/src/async/12_transition.md index 59b3f36ef..bc52b30c3 100644 --- a/docs/book/src/async/12_transition.md +++ b/docs/book/src/async/12_transition.md @@ -29,13 +29,13 @@ async fn important_api_call(id: usize) -> String { } #[component] -fn App(cx: Scope) -> impl IntoView { - let (tab, set_tab) = create_signal(cx, 0); +fn App() -> impl IntoView { + let (tab, set_tab) = create_signal(0); // this will reload every time `tab` changes - let user_data = create_resource(cx, tab, |tab| async move { important_api_call(tab).await }); + let user_data = create_resource(tab, |tab| async move { important_api_call(tab).await }); - view! { cx, + view! {
    diff --git a/docs/book/src/reactivity/working_with_signals.md b/docs/book/src/reactivity/working_with_signals.md index 665b91b73..fe9ee4d64 100644 --- a/docs/book/src/reactivity/working_with_signals.md +++ b/docs/book/src/reactivity/working_with_signals.md @@ -14,7 +14,7 @@ There are four basic signal operations: Calling a `ReadSignal` as a function is syntax sugar for `.get()`. Calling a `WriteSignal` as a function is syntax sugar for `.set()`. So ```rust -let (count, set_count) = create_signal(cx, 0); +let (count, set_count) = create_signal(0); set_count(1); log!(count()); ``` @@ -22,7 +22,7 @@ log!(count()); is the same as ```rust -let (count, set_count) = create_signal(cx, 0); +let (count, set_count) = create_signal(0); set_count.set(1); log!(count.get()); ``` @@ -36,7 +36,7 @@ However, there are some very good use cases for `.with()` and `.update()`. For example, consider a signal that holds a `Vec`. ```rust -let (names, set_names) = create_signal(cx, Vec::new()); +let (names, set_names) = create_signal(Vec::new()); if names().is_empty() { set_names(vec!["Alice".to_string()]); } @@ -47,7 +47,7 @@ In terms of logic, this is simple enough, but it’s hiding some significant ine Likewise, `set_names` replaces the value with a whole new `Vec<_>`. This is fine, but we might as well just mutate the original `Vec<_>` in place. ```rust -let (names, set_names) = create_signal(cx, Vec::new()); +let (names, set_names) = create_signal(Vec::new()); if names.with(|names| names.is_empty()) { set_names.update(|names| names.push("Alice".to_string())); } @@ -70,33 +70,39 @@ After all, `.with()` simply takes a function that takes the value by reference. Often people ask about situations in which some signal needs to change based on some other signal’s value. There are three good ways to do this, and one that’s less than ideal but okay under controlled circumstances. ### Good Options + **1) B is a function of A.** Create a signal for A and a derived signal or memo for B. ```rust -let (count, set_count) = create_signal(cx, 1); +let (count, set_count) = create_signal(1); let derived_signal_double_count = move || count() * 2; -let memoized_double_count = create_memo(cx, move |_| count() * 2); +let memoized_double_count = create_memo(move |_| count() * 2); ``` + > For guidance on whether to use a derived signal or a memo, see the docs for [`create_memo`](https://docs.rs/leptos/latest/leptos/fn.create_memo.html) -> -**2) C is a function of A and some other thing B.** Create signals for A and B and a derived signal or memo for C. +> +> **2) C is a function of A and some other thing B.** Create signals for A and B and a derived signal or memo for C. ```rust -let (first_name, set_first_name) = create_signal(cx, "Bridget".to_string()); -let (last_name, set_last_name) = create_signal(cx, "Jones".to_string()); +let (first_name, set_first_name) = create_signal("Bridget".to_string()); +let (last_name, set_last_name) = create_signal("Jones".to_string()); let full_name = move || format!("{} {}", first_name(), last_name()); ``` + **3) A and B are independent signals, but sometimes updated at the same time.** When you make the call to update A, make a separate call to update B. + ```rust -let (age, set_age) = create_signal(cx, 32); -let (favorite_number, set_favorite_number) = create_signal(cx, 42); +let (age, set_age) = create_signal(32); +let (favorite_number, set_favorite_number) = create_signal(42); // use this to handle a click on a `Clear` button let clear_handler = move |_| { set_age(0); set_favorite_number(0); }; ``` + ### If you really must... + **4) Create an effect to write to B whenever A changes.** This is officially discouraged, for several reasons: a) It will always be less efficient, as it means every time A updates you do two full trips through the reactive process. (You set A, which causes the effect to run, as well as any other effects that depend on A. Then you set B, which causes any effects that depend on B to run.) b) It increases your chances of accidentally creating things like infinite loops or over-re-running effects. This is the kind of ping-ponging, reactive spaghetti code that was common in the early 2010s and that we try to avoid with things like read-write segregation and discouraging writing to signals from effects. diff --git a/docs/book/src/router/16_routes.md b/docs/book/src/router/16_routes.md index 2373e80f8..927c668a7 100644 --- a/docs/book/src/router/16_routes.md +++ b/docs/book/src/router/16_routes.md @@ -33,8 +33,8 @@ use leptos::*; use leptos_router::*; #[component] -pub fn App(cx: Scope) -> impl IntoView { - view! { cx, +pub fn App() -> impl IntoView { + view! {
    }/> // if no id specified, fall back - "Select a user to view contact info."
    @@ -229,8 +230,8 @@ fn App(cx: Scope) -> impl IntoView { } #[component] -fn ContactList(cx: Scope) -> impl IntoView { - view! { cx, +fn ContactList() -> impl IntoView { + view! {
    // here's our contact list component itself
    @@ -249,9 +250,9 @@ fn ContactList(cx: Scope) -> impl IntoView { } #[component] -fn ContactInfo(cx: Scope) -> impl IntoView { +fn ContactInfo() -> impl IntoView { // we can access the :id param reactively with `use_params_map` - let params = use_params_map(cx); + let params = use_params_map(); let id = move || params.with(|params| params.get("id").cloned().unwrap_or_default()); // imagine we're loading data from an API here @@ -262,7 +263,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { _ => "User not found.", }; - view! { cx, + view! {

    {name}

    @@ -278,7 +279,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/router/18_params_and_queries.md b/docs/book/src/router/18_params_and_queries.md index f13f41cb6..a3e0bab04 100644 --- a/docs/book/src/router/18_params_and_queries.md +++ b/docs/book/src/router/18_params_and_queries.md @@ -50,8 +50,8 @@ Now we can use them in a component. Imagine a URL that has both params and a que The typed versions return `Memo>`. It’s a Memo so it reacts to changes in the URL. It’s a `Result` because the params or query need to be parsed from the URL, and may or may not be valid. ```rust -let params = use_params::(cx); -let query = use_query::(cx); +let params = use_params::(); +let query = use_query::(); // id: || -> usize let id = move || { @@ -66,8 +66,8 @@ let id = move || { The untyped versions return `Memo`. Again, it’s memo to react to changes in the URL. [`ParamsMap`](https://docs.rs/leptos_router/0.2.3/leptos_router/struct.ParamsMap.html) behaves a lot like any other map type, with a `.get()` method that returns `Option<&String>`. ```rust -let params = use_params_map(cx); -let query = use_query_map(cx); +let params = use_params_map(); +let query = use_query_map(); // id: || -> Option let id = move || { @@ -94,8 +94,8 @@ use leptos::*; use leptos_router::*; #[component] -fn App(cx: Scope) -> impl IntoView { - view! { cx, +fn App() -> impl IntoView { + view! {

    "Contact App"

    // this
    }/> - "(Conversations)"
    }/> // if no id specified, fall back - "Select a user to view contact info."
    @@ -145,8 +145,8 @@ fn App(cx: Scope) -> impl IntoView { } #[component] -fn ContactList(cx: Scope) -> impl IntoView { - view! { cx, +fn ContactList() -> impl IntoView { + view! {
    // here's our contact list component itself
    @@ -165,9 +165,9 @@ fn ContactList(cx: Scope) -> impl IntoView { } #[component] -fn ContactInfo(cx: Scope) -> impl IntoView { +fn ContactInfo() -> impl IntoView { // we can access the :id param reactively with `use_params_map` - let params = use_params_map(cx); + let params = use_params_map(); let id = move || params.with(|params| params.get("id").cloned().unwrap_or_default()); // imagine we're loading data from an API here @@ -178,7 +178,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { _ => "User not found.", }; - view! { cx, + view! {

    {name}

    @@ -194,7 +194,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/router/19_a.md b/docs/book/src/router/19_a.md index 2d893a5b0..bcdc49110 100644 --- a/docs/book/src/router/19_a.md +++ b/docs/book/src/router/19_a.md @@ -23,8 +23,9 @@ The router also provides an [``](https://docs.rs/leptos_router/latest/leptos_ Your most-used methods of navigating between pages should be with `` and `` elements or with the enhanced `` and `` components. Using links and forms to navigate is the best solution for accessibility and graceful degradation. On occasion, though, you’ll want to navigate programmatically, i.e., call a function that can navigate to a new page. In that case, you should use the [`use_navigate`](https://docs.rs/leptos_router/latest/leptos_router/fn.use_navigate.html) function. + ```rust -let navigate = leptos_router::use_navigate(cx); +let navigate = leptos_router::use_navigate(); navigate("/somewhere", Default::default()); ``` @@ -46,8 +47,8 @@ use leptos::*; use leptos_router::*; #[component] -fn App(cx: Scope) -> impl IntoView { - view! { cx, +fn App() -> impl IntoView { + view! {

    "Contact App"

    // this
    }/> - "(Conversations)"
    }/> // if no id specified, fall back - "Select a user to view contact info."
    @@ -97,8 +98,8 @@ fn App(cx: Scope) -> impl IntoView { } #[component] -fn ContactList(cx: Scope) -> impl IntoView { - view! { cx, +fn ContactList() -> impl IntoView { + view! {
    // here's our contact list component itself
    @@ -117,9 +118,9 @@ fn ContactList(cx: Scope) -> impl IntoView { } #[component] -fn ContactInfo(cx: Scope) -> impl IntoView { +fn ContactInfo() -> impl IntoView { // we can access the :id param reactively with `use_params_map` - let params = use_params_map(cx); + let params = use_params_map(); let id = move || params.with(|params| params.get("id").cloned().unwrap_or_default()); // imagine we're loading data from an API here @@ -130,7 +131,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { _ => "User not found.", }; - view! { cx, + view! {

    {name}

    @@ -146,7 +147,7 @@ fn ContactInfo(cx: Scope) -> impl IntoView { } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/router/20_form.md b/docs/book/src/router/20_form.md index feb129aae..f1bc1cbfe 100644 --- a/docs/book/src/router/20_form.md +++ b/docs/book/src/router/20_form.md @@ -24,15 +24,15 @@ async fn fetch_results() { } #[component] -pub fn FormExample(cx: Scope) -> impl IntoView { +pub fn FormExample() -> impl IntoView { // reactive access to URL query strings - let query = use_query_map(cx); + let query = use_query_map(); // search stored as ?q= let search = move || query().get("q").cloned().unwrap_or_default(); // a resource driven by the search string - let search_results = create_resource(cx, search, fetch_results); + let search_results = create_resource(search, fetch_results); - view! { cx, + view! { @@ -51,7 +51,7 @@ This is a great pattern. The data flow is extremely clear: all data flows from t We can actually take it a step further and do something kind of clever: ```rust -view! { cx, +view! { impl IntoView { - view! { cx, +fn App() -> impl IntoView { + view! {

    ""

    @@ -88,14 +88,14 @@ fn App(cx: Scope) -> impl IntoView { } #[component] -pub fn FormExample(cx: Scope) -> impl IntoView { +pub fn FormExample() -> impl IntoView { // reactive access to URL query - let query = use_query_map(cx); + let query = use_query_map(); let name = move || query().get("name").cloned().unwrap_or_default(); let number = move || query().get("number").cloned().unwrap_or_default(); let select = move || query().get("select").cloned().unwrap_or_default(); - view! { cx, + view! { // read out the URL query strings @@ -172,7 +172,7 @@ pub fn FormExample(cx: Scope) -> impl IntoView { } fn main() { - leptos::mount_to_body(|cx| view! { cx, }) + leptos::mount_to_body(|| view! { }) } ``` diff --git a/docs/book/src/server/25_server_functions.md b/docs/book/src/server/25_server_functions.md index da8ab140c..06568a329 100644 --- a/docs/book/src/server/25_server_functions.md +++ b/docs/book/src/server/25_server_functions.md @@ -31,9 +31,9 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> { } #[component] -pub fn BusyButton(cx: Scope) -> impl IntoView { +pub fn BusyButton() -> impl IntoView { view! { - cx, +