This is a simplified implementation of the SwiftUI Composable Architecture (TCA) for the Rust Dioxus Library. There're many similarities to the Elm architecture as well as various React Redux models. This is currently being used for the Ebou Mastodon App. It is an early alpha.
- Reducers
- Actions
- Environment
- Nesting & Combining Child Reducers
- Sending messages to child reducers or from child reducers to the parent reducers
- View Stores which handle the View State
There's a super simple example in the examples
folder to see what it does. You can run it via:
cargo run --example gnarl
Navicula splits up code into
- State: All your view / logic state
- Reducer: All the logic
- Action: The actions that can be performed in a reducer
- View: The view. Any action here is sent to the reducer and the reducer and then the view is rerendered
The reducer expects that each handled action returns an Effect
. There're multiple Effect types:
- Execute a Future
- Execute another Action
- Execute an Effect with some delay
- Run a timer that continously polls the reducer
- Merge multiple Effects together
- Perform javascript in the UI context
Here's an example of a simple reducer function:
pub enum Action {
Initial,
Close,
Chats(usize),
Edit(Message),
Edit2(Message),
FinishEdit,
}
fn reduce<'a, 'b>(
context: &'a impl MessageContext<Self::Action, Self::DelegateMessage, Self::Message>,
action: Self::Action,
_state: &'a mut Self::State,
environment: &'a Self::Environment,
) -> Effect<'b, Self::Action> {
match action {
Action::Initial => {
// fake subscription, just to see if drop works
return environment
.chats
.subscribe("chat-chats", context, |data| Action::Chats(data.len()));
}
Action::Chats(cnt) => {
log::info!("Have {cnt} chats");
}
Action::Edit(message) => {
return Effect::action(Action::Edit2(message)).delay(Duration::from_secs(2))
}
Action::Edit2(message) => {
environment
.selected
.with_mutation(|mut s| *s = Some(message));
}
Action::FinishEdit => {
environment.selected.with_mutation(|mut s| *s = None);
}
Action::Close => {
context.send_parent(DelegateMessage::Closed);
}
}
Effect::NONE
}
Reducers can be nested to form hierachies and have three message / action types:
Message
: This is send to child reducersDelegateMessage
: This is send back to the parent reducer of this reducerAction
: This is the internal message in the reducer / view
You can see more by checking out the example.
Navicula is licensed under the MIT License