Attribute Macro intuitive::component

source ·
#[component]
Expand description

Helper attribute macro for creating functional components.

Usage

This macro is used when creating functional components, where the name of the generated component is the item in the attribute. For example,

#[component(Root)]
pub fn render(title: String) {
  let text = use_state(String::new);

  let on_key = on_key! { [text]
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.pop()),
    KeyEvent { code: Esc, .. } => event::quit(),
  };

  render! {
    Centered() {
      Section(title) {
        Text(text: text.get(), on_key)
      }
    }
  }
}

constructs a Root component, that can be used in a render! macro.

Parameters

If the render function contains parameters, these will become parameters to the generated component. These parameters can later be supplied when using the generated component in a render! macro. The parameters’ types must implement Default, as the generated component derives Default. If you need more control over the default values of the parameters, consider implementing the Component trait instead of using the #[component(..)] attribute macro.

Managing State

State in functional components is managed similarly to how they are in React, using the use_state hook. Refer to the use_state documentation for details.

Handling Key Events

In functional components, key events are sent to the component at the root of the returned render! macro invocation. This means that in the example above, the key event will be sent to an instance of the Centered component. However, most components forward their key events to their children (especially those that have only a single child), and therefore the on_key handler could have been provided to any of the Centered, Section, or Text components above.

Generics

When requiring generics, for example when accepting a variable number of children, they can be added into the attribute and then used in the parameters. For example:

#[component(Root<const N: usize>)]
pub fn render(title: String, children: Children<N>) {
  let text = use_state(String::new);

  let on_key = on_key! { [text]
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.pop()),
    KeyEvent { code: Esc, .. } => event::quit(),
  };

  render! {
    Centered() {
      Section(title) {
        Text(text: text.get(), on_key)
      }
    }
  }
}

Generated Component

The generated component is a structure that implements the Component trait. It also has a an associated function new() -> component::Any that is used to create the component when passing it to Terminal::new(). If the component has parameters, they will also be parameters to the associated function new()in the same order they were specified in the render function.

Nuances

There are a couple of nuances with this macro:

  • The visibility of the generated component will be the same as that of the render function the #[component(..)] attribute is applied to.
  • The return type to render (and even the function name itself) are completely ignored. In order to keep things consistent, it’s recommended that the function is called render and the return type is left empty.

Helper attribute macro for creating functional components.

See the documentation in the intuitive crate for details.