Create list of common bugs (includes #82 and #83)

This commit is contained in:
Greg Johnston 2022-11-17 18:20:54 -05:00
parent f87ac34656
commit d6c6ab7939
2 changed files with 96 additions and 2 deletions

View File

@ -59,7 +59,8 @@ Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained re
Here are some resources for learning more about Leptos:
- [Examples](https://github.com/gbj/leptos/tree/main/examples)
- [API Documentation](https://docs.rs/leptos/latest/leptos/) (in progress)
- [API Documentation](https://docs.rs/leptos/latest/leptos/)
- [Common Bugs](https://github.com/gbj/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- Leptos Guide (in progress)
## `nightly` Note
@ -107,6 +108,7 @@ The gold standard for testing raw rendering performance for front-end web framew
### Can I use this for native GUI?
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive native any GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
- Use signals, derived signals, and memos to create your reactive system
- Create GUI widgets
- Use event listeners to update signals
@ -131,7 +133,7 @@ There are some practical differences that make a significant difference:
- **Maturity:** Sycamore is obviously a much more mature and stable library with a larger ecosystem.
- **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.
- **Template node cloning:** Leptos's `view` macro compiles to a static HTML string and a set of instructions of how to assign its reactive values. This means that at runtime, Leptos can clone a `<template>` node rather than calling `document.createElement()` to create DOM nodes. This is a _significantly_ faster way of rendering components.
- **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` 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(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` 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

92
docs/COMMON_BUGS.md Normal file
View File

@ -0,0 +1,92 @@
# Leptos Gotchas: Common Bugs
This document is intended as a running list of common issues, with example code and solutions.
## Reactivity
### Don't get and set a signal within the same effect
**Issue**: Sometimes you need access to a signal's current value when setting a new value.
```rust,should_panic
let (a, set_a) = create_signal(cx, false);
create_effect(cx, move |_| {
if !a() {
set_a(true); // ❌ panics: already borrowed
}
});
```
**Solution**: Use the `.update()` function instead.
```rust
let (a, set_a) = create_signal(cx, false);
create_effect(cx, move |_| {
// ✅ updates the signal, which provides you with the current value
set_a.update(|a: &mut bool| {
if *a {
*a = false;
}
})
});
```
### Avoid writing to a signal from an effect
**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_a) = create_signal(cx, false);
create_effect(cx, move |_| {
if a() > 5 {
set_b(true);
}
});
```
This creates an inefficient chain of updates, and can easily lead to infinite loops in more complex applications.
**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 b = move || a () > 5;
```
## Templates and the DOM
### `<input value=...>` doesn't update or stops updating
Many DOM attributes can be updated either by setting an attribute on the DOM node, or by setting an object property directly on it. In general, `setAttribute()` stops working once the property has been set.
This means that in practice, attributes like `value` or `checked` on an `<input/>` element only update the _default_ value for the `<input/>`. 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 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
<input value=a on:input=on_input />
<input value=a on:input=on_input />
}
```
```rust
let (a, set_a) = create_signal(cx, "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*
<input prop:value=a on:input=on_input />
<input prop:value=a on:input=on_input />
}
```