Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

Follow publication

Implementing Redux With Rust

Radovan Stevanovic
Level Up Coding
Published in
5 min readDec 20, 2022

--

Basic Implementation

struct TodoState {
todos: Vec<String>,
}
trait TodoAction {
fn apply(&self, state: &mut TodoState);
}
fn todo_reducer(state: &TodoState, action: &dyn TodoAction) -> TodoState {
let mut new_state = state.clone();
action.apply(&mut new_state);
new_state
}
struct AddTodoAction {
todo: String,
}

impl TodoAction for AddTodoAction {
fn apply(&self, state: &mut TodoState) {
state.todos.push(self.todo.clone());
}
}
struct RemoveTodoAction {
index: usize,
}

impl TodoAction for RemoveTodoAction {
fn apply(&self, state: &mut TodoState) {
state.todos.remove(self.index);
}
}
let mut state = TodoState { todos: vec![] };
let action = AddTodoAction { todo: "Learn Rust".to_string() };
state = todo_reducer(&state, &action);

Using a macro to generate the reducer function and enums to represent actions

enum TodoAction {
AddTodo(String),
RemoveTodo(usize),
}
#[macro_export]
macro_rules! create_reducer {
($state_type:ty, $action_type:ty, $reducer_fn:expr) => {
fn reducer(state: &$state_type, action: $action_type) -> $state_type {
let mut new_state = state.clone();
$reducer_fn(&mut new_state, action);
new_state
}
}
}
create_reducer!(TodoState, TodoAction, |state: &mut TodoState, action| {
match action {
TodoAction::AddTodo(todo) => state.todos.push(todo),
TodoAction::RemoveTodo(index) => state.todos.remove(index),
}
});
let mut state = TodoState { todos: vec![] };
let action = TodoAction::AddTodo("Learn Rust".to_string());
state = reducer(&state, action);

Now we can use Yew to build the UI for our to-do application

use yew::{html, Callback, Html};

struct TodoItem {
todo: String,
on_remove: Callback<()>,
}

impl Component for TodoItem {
type Message = ();
type Properties = Self;

fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
Self {
todo: props.todo,
on_remove: props.on_remove,
}
}

fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}

fn view(&self) -> Html {
html! {
<div>
<span>{ &self.todo }</span>
<button onclick=self.on_remove.clone()>{"Remove"}</button>
</div>
}
}
}
use yew::{html, Callback, Component, ComponentLink, Html, ShouldRender};
use yew_functional::{use_state, use_reducer};

struct TodoApp {
link: ComponentLink<Self>,
state: TodoState,
dispatch: Callback<TodoAction>,
}

impl Component for TodoApp {
type Message = ();
type Properties = ();

fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
let state = TodoState { todos: vec![] };
let (dispatch, _) = use_reducer(link, reducer, state);
Self { link, state, dispatch }
}

fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}

fn view(&self) -> Html {
html! {
<div>
<h1>{"Todo List"}</h1>
<ul>
{ for self.state.todos.iter().enumerate().map(|(index, todo)| {
html! {
<TodoItem
todo=todo.clone()
on_remove=self.link.callback(move |_| {
self.dispatch.emit(TodoAction::RemoveTodo(index))
})
/>
}
}) }
</ul>
<form onsubmit=self.link.callback(|event| {
event.prevent_default();
let input = event.target().unwrap().try_into::<HtmlInputElement>().unwrap();
let todo = input.value();
input.set_value("");
self.dispatch.emit(TodoAction::AddTodo(todo))
})>
<input type="text" />
<button type="submit">{"Add Todo"}</button>
</form>
</div>
}
}
use yew::{html, App};

fn main() {
yew::initialize();
App::<TodoApp>::new().mount_to_body();
yew::run_loop();
}

Level Up Coding

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Written by Radovan Stevanovic

Curiosity in functional programming, cybersecurity, and blockchain drives me to create innovative solutions and continually improve my skills

No responses yet

Write a response