Callbacks (deprecated) ..TODO

#[prop(into)] set_value: Callback<u32>, #[prop(into, optional)] maybe_render: Option<Callback<(Scope, String), View>> ... set_value.call(42); if let Some(render) = maybe_render { let _view = render.call(("foo".to_owned())); }

Easy, isn't it?

Callbacks can sometimes be tricky in Leptos. When realized with generics, they are easy to use as a component-consumer and fast, but hard(er) to write as an author and, because of their generic nature, increase WASM binary size.

If the property of a component specifying a callback should be Optional, the generic approach is of no help, as Rust will have problems inferring unspecified, and not otherwise explicitly nameable, types for the properties left out when instantiating such a component.

An additional burden with the generic approach: Copy. A simple generic Fn prop can only be moved once inside your component when not specified with an additional Copy bound. But doing so will limit your users ability to write compiling closures for the callback, as only closures not taking ownership of non-copyable-values, implicitly making the closure copy, can be used.

If you, as a component author, need to move a callback into more places, you can put it inside a call to store_value to get an easily copyable and therefore moveable value. Using a generic Fn in the non-optional case or a Box<dyn Fn> in case the prop is optional and always storing it boxed in a StoredValue is good solution. Arguments could be made that

  • (a) components are not often recreated, as we always want to reactively updated their content instead
  • (b) some callbacks aren't called often, as they are only used to propagate user input and users are incredibly slow compared to machines.

These make both the performance hit of creating a Box<dyn Fn> and calling it later somewhat irrelevant. The penalty of managing an extra StoredValue through Leptos should not be overlooked though.

Because creating boxed closures manually and storing them in is not ergonomic, Leptonic provides an easy to use Callback type, which is defined as

pub struct Callback<T: 'static, R: 'static = ()>(leptos::StoredValue<Box<dyn Fn(T) -> R>>);

This callback type is both Clone and Copy and can be used at multiple locations in a component without requiring an explicitly Clone/Copy closure to be provided by the user of the component.

Some Leptonic components use this type for some of their callbacks when the generic approach was not ergonomic. You can also use the Callback type in your own components props. For example:

#[prop(into, optional)] set_value: Option<Callback<String>>

As seen in the definition. Two generic arguments are available. Use a tuple for T if the callback should receive multiple values. Provide the second type, otherwise defaulting to (), when the callback should return a value.

#[prop(into, optional)] render: Option<Callback<(Scope, MyType), View>>

Create it when instantiating your component using the create_callback convenience function.

view! { <MyComponent set_value=create_callback(move |v| {}) /> }

Calling a callback in a component is as simple as this:

set_value.call("foo".to_owned())

Passing the callback further down to children is no problem.

There are two drawbacks though:

  • Callbacks cannot take (non-static) references, only owned values.
  • If a child requires a callback of slightly changed signature, you have to pay for creating an intermediate callback..