mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
docs: added Callback
to documentation and examples. (#1926)
* added Callback to documentation and examples. Also reduced code duplication in Callback implementation. * added back the closure event callback example
This commit is contained in:
parent
05b4f8e617
commit
e2f6780de4
9 changed files with 97 additions and 84 deletions
|
@ -72,9 +72,7 @@ pub fn App() -> impl IntoView {
|
|||
|
||||
|
||||
#[component]
|
||||
pub fn ButtonB<F>(on_click: F) -> impl IntoView
|
||||
where
|
||||
F: Fn(MouseEvent) + 'static,
|
||||
pub fn ButtonB(#[prop(into)] on_click: Callback<MouseEvent>) -> impl IntoView
|
||||
{
|
||||
view! {
|
||||
<button on:click=on_click>
|
||||
|
@ -90,10 +88,50 @@ of keeping local state local, preventing the problem of spaghetti mutation. But
|
|||
the logic to mutate that signal needs to exist up in `<App/>`, not down in `<ButtonB/>`. These
|
||||
are real trade-offs, not a simple right-or-wrong choice.
|
||||
|
||||
> Note the way we use the `Callback<In, Out>` type. This is basically a
|
||||
> wrapper around a closure `Fn(In) -> Out` that is also `Copy` and makes it
|
||||
> easy to pass around.
|
||||
>
|
||||
> We also used the `#[prop(into)]` attribute so we can pass a normal closure into
|
||||
> `on_click`. Please see the [chapter "`into` Props"](./03_components.md#into-props) for more details.
|
||||
|
||||
### 2.1 Use Closure instead of `Callback`
|
||||
|
||||
You can use a Rust closure `Fn(MouseEvent)` directly instead of `Callback`:
|
||||
|
||||
```rust
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
let (toggled, set_toggled) = create_signal(false);
|
||||
view! {
|
||||
<p>"Toggled? " {toggled}</p>
|
||||
<ButtonB on_click=move |_| set_toggled.update(|value| *value = !*value)/>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[component]
|
||||
pub fn ButtonB<F>(on_click: F) -> impl IntoView
|
||||
where
|
||||
F: Fn(MouseEvent) + 'static,
|
||||
pub fn ButtonB(#[prop(into)] on_click: Callback<MouseEvent>) -> impl IntoView
|
||||
{
|
||||
view! {
|
||||
<button on:click=on_click>
|
||||
"Toggle"
|
||||
</button>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The code is very similar in this case. On more advanced use-cases using a
|
||||
closure might require some cloning compared to using a `Callback`.
|
||||
|
||||
> Note the way we declare the generic type `F` here for the callback. If you’re
|
||||
> confused, look back at the [generic props](./03_components.html#generic-props) section
|
||||
> of the chapter on components.
|
||||
|
||||
|
||||
## 3. Use an Event Listener
|
||||
|
||||
You can actually write Option 2 in a slightly different way. If the callback maps directly onto
|
||||
|
|
3
examples/counters/rust-toolchain.toml
Normal file
3
examples/counters/rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
[toolchain]
|
||||
channel = "nightly"
|
|
@ -3,10 +3,10 @@ use leptos::*;
|
|||
use leptos_router::*;
|
||||
|
||||
#[component]
|
||||
pub fn NavBar<F>(logged_in: Signal<bool>, on_logout: F) -> impl IntoView
|
||||
where
|
||||
F: Fn() + 'static + Clone,
|
||||
{
|
||||
pub fn NavBar(
|
||||
logged_in: Signal<bool>,
|
||||
#[prop(into)] on_logout: Callback<()>,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
<nav>
|
||||
<Show
|
||||
|
@ -21,10 +21,7 @@ where
|
|||
>
|
||||
<a
|
||||
href="#"
|
||||
on:click={
|
||||
let on_logout = on_logout.clone();
|
||||
move |_| on_logout()
|
||||
}
|
||||
on:click=move |_| on_logout.call(())
|
||||
>
|
||||
"Logout"
|
||||
</a>
|
||||
|
|
|
@ -57,7 +57,7 @@ pub fn App() -> impl IntoView {
|
|||
|
||||
// -- callbacks -- //
|
||||
|
||||
let on_logout = move || {
|
||||
let on_logout = move |_| {
|
||||
logout.dispatch(());
|
||||
};
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ use leptos::*;
|
|||
use leptos_router::*;
|
||||
|
||||
#[component]
|
||||
pub fn Login<F>(api: UnauthorizedApi, on_success: F) -> impl IntoView
|
||||
where
|
||||
F: Fn(AuthorizedApi) + 'static + Clone,
|
||||
{
|
||||
pub fn Login(
|
||||
api: UnauthorizedApi,
|
||||
#[prop(into)] on_success: Callback<AuthorizedApi>,
|
||||
) -> impl IntoView {
|
||||
let (login_error, set_login_error) = create_signal(None::<String>);
|
||||
let (wait_for_response, set_wait_for_response) = create_signal(false);
|
||||
|
||||
|
@ -21,7 +21,6 @@ where
|
|||
let email = email.to_string();
|
||||
let password = password.to_string();
|
||||
let credentials = Credentials { email, password };
|
||||
let on_success = on_success.clone();
|
||||
async move {
|
||||
set_wait_for_response.update(|w| *w = true);
|
||||
let result = api.login(&credentials).await;
|
||||
|
@ -29,7 +28,7 @@ where
|
|||
match result {
|
||||
Ok(res) => {
|
||||
set_login_error.update(|e| *e = None);
|
||||
on_success(res);
|
||||
on_success.call(res);
|
||||
}
|
||||
Err(err) => {
|
||||
let msg = match err {
|
||||
|
|
3
examples/parent_child/rust-toolchain.toml
Normal file
3
examples/parent_child/rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
[toolchain]
|
||||
channel = "nightly"
|
|
@ -74,13 +74,11 @@ pub fn ButtonA(
|
|||
|
||||
/// Button B receives a closure
|
||||
#[component]
|
||||
pub fn ButtonB<F>(
|
||||
pub fn ButtonB(
|
||||
/// Callback that will be invoked when the button is clicked.
|
||||
on_click: F,
|
||||
) -> impl IntoView
|
||||
where
|
||||
F: Fn(MouseEvent) + 'static,
|
||||
{
|
||||
#[prop(into)]
|
||||
on_click: Callback<MouseEvent>,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
|
||||
<button
|
||||
|
@ -89,18 +87,6 @@ where
|
|||
"Toggle Right"
|
||||
</button>
|
||||
}
|
||||
|
||||
// just a note: in an ordinary function ButtonB could take on_click: impl Fn(MouseEvent) + 'static
|
||||
// and save you from typing out the generic
|
||||
// the component macro actually expands to define a
|
||||
//
|
||||
// struct ButtonBProps<F> where F: Fn(MouseEvent) + 'static {
|
||||
// on_click: F
|
||||
// }
|
||||
//
|
||||
// this is what allows us to have named props in our component invocation,
|
||||
// instead of an ordered list of function arguments
|
||||
// if Rust ever had named function arguments we could drop this requirement
|
||||
}
|
||||
|
||||
/// Button C is a dummy: it renders a button but doesn't handle
|
||||
|
|
|
@ -43,6 +43,7 @@ cfg-if = "1"
|
|||
indexmap = "2"
|
||||
self_cell = "1.0.0"
|
||||
pin-project = "1"
|
||||
paste = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
|
|
|
@ -107,31 +107,41 @@ impl<In: 'static, Out: 'static> Callable<In, Out> for Callback<In, Out> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
impl<F, In, T, Out> From<F> for Callback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> Callback<In, Out> {
|
||||
Callback::new(move |x| f(x).into())
|
||||
}
|
||||
macro_rules! impl_from_fn {
|
||||
($ty:ident) => {
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
impl<F, In, T, Out> From<F> for $ty<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
paste::paste! {
|
||||
#[cfg(feature = "nightly")]
|
||||
auto trait [<NotRaw $ty>] {}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<A, B> ![<NotRaw $ty>] for $ty<A, B> {}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<F, In, T, Out> From<F> for $ty<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + [<NotRaw $ty>] + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
auto trait NotRawCallback {}
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<A, B> !NotRawCallback for Callback<A, B> {}
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<F, In, T, Out> From<F> for Callback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + NotRawCallback + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> Callback<In, Out> {
|
||||
Callback::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
impl_from_fn!(Callback);
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnOnce<(In,)> for Callback<In, Out> {
|
||||
|
@ -190,31 +200,7 @@ impl<In: 'static, Out: 'static> SyncCallback<In, Out> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
impl<F, In, T, Out> From<F> for SyncCallback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> SyncCallback<In, Out> {
|
||||
SyncCallback::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
auto trait NotRawSyncCallback {}
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<A, B> !NotRawSyncCallback for SyncCallback<A, B> {}
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<F, In, T, Out> From<F> for SyncCallback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + NotRawSyncCallback + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
{
|
||||
fn from(f: F) -> SyncCallback<In, Out> {
|
||||
SyncCallback::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
impl_from_fn!(SyncCallback);
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnOnce<(In,)> for SyncCallback<In, Out> {
|
||||
|
|
Loading…
Reference in a new issue