src/content/docs/concepts/application-patterns/component-architecture.md
If you are interested in a more object oriented approach to organizing TUIs, you can use a
Component based approach.
A couple of projects in the wild use this approach
We also have a component template that has an example of this Component based approach:
We already covered TEA in the previous section. The Component
architecture takes a slightly more object oriented trait based approach.
Each component encapsulates its own state, event handlers, and rendering logic.
Component Initialization (init) - This is where a component can set up any initial state or
resources it needs. It’s a separate process from handling events or rendering.
Event Handling (handle_events, handle_key_events, handle_mouse_events) - Each component has
its own event handlers. This allows for a finer-grained approach to event handling, with each
component only dealing with the events it's interested in. This contrasts with Elm's single
update function that handles messages for the entire application.
State Update (update) - Components can have their own local state and can update it in response
to actions. This state is private to the component, which differs from Elm's global model.
Rendering (render) - Each component defines its own rendering logic. It knows how to draw
itself, given a rendering context. This is similar to Elm's view function but on a
component-by-component basis.
Here's an example of the Component trait implementation you might use:
use color_eyre::eyre::Result;
use ratatui::crossterm::event::{KeyEvent, MouseEvent};
use ratatui::layout::Rect;
use crate::{action::Action, event::Event, terminal::Frame};
pub trait Component {
fn init(&mut self) -> Result<()> {
Ok(())
}
fn handle_events(&mut self, event: Option<Event>) -> Action {
match event {
Some(Event::Quit) => Action::Quit,
Some(Event::Tick) => Action::Tick,
Some(Event::Key(key_event)) => self.handle_key_events(key_event),
Some(Event::Mouse(mouse_event)) => self.handle_mouse_events(mouse_event),
Some(Event::Resize(x, y)) => Action::Resize(x, y),
Some(_) => Action::Noop,
None => Action::Noop,
}
}
fn handle_key_events(&mut self, key: KeyEvent) -> Action {
Action::Noop
}
fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Action {
Action::Noop
}
fn update(&mut self, action: Action) -> Action {
Action::Noop
}
fn render(&mut self, f: &mut Frame, rect: Rect);
}
One advantage of this approach is that it incentivizes co-locating the handle_events, update and
render functions on a component level.