Counter example part:
// ------ ------
// Update
// ------ ------
// enum Msg ...
// `update` describes how to handle each `Msg`.
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
match msg {
Msg::Increment => *model += 1,
}
}
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg {
Msg::UrlChanged(subs::UrlChanged(url)) => {
model.page = Page::init(
url,
&model.guides,
&mut model.selected_seed_version,
);
let title = match model.page {
Page::Guide {
guide,
..
} => format!("{} - {}", guide.menu_title, TITLE_SUFFIX),
Page::NotFound => format!("404 - {}", TITLE_SUFFIX),
};
document().set_title(&title);
orders.send_msg(Msg::ScrollToTop);
},
Msg::ScrollToTop => window().scroll_to_with_scroll_to_options(
web_sys::ScrollToOptions::new().top(0.),
),
Msg::ToggleGuideList => model.guide_list_visibility.toggle(),
Msg::HideGuideList => {
model.guide_list_visibility = Hidden;
},
Msg::ToggleMenu => model.menu_visibility.toggle(),
Msg::HideMenu => {
model.menu_visibility = Hidden;
},
Msg::SearchQueryChanged(query) => {
model.matched_guides = search(&model.guides, &query);
model.search_query = query;
},
Msg::ToggleMode => {
model.mode.toggle();
let config = Config {
mode: model.mode,
};
LocalStorage::insert(STORAGE_KEY, &config)
.expect("insert to local storage");
},
Msg::SwitchVersion(version) => {
orders
.notify(subs::UrlRequested::new(
model
.base_url
.clone()
.add_path_part(version.version())
.add_path_part(DEFAULT_GUIDE_SLUG),
))
.skip();
},
}
}
update is the function where you should handle Msgs.
It's the only place where you should mutate your business data.
update is invoked by Seed when it receives a new Msg instance.
updateIt's basically just one match - keep it simple.
It will be probably the longest function in your app. The most attempts to shorten it fail and make the code worse. Make sure that your update helpers are really necessary.
When you need to write some helpers, respect the rule "children below the parent" as always.
Don't write catch-all match arm. (It's a general rule for the entire code-base; you would regret it sooner or later.)
It's often useful to handle one Msg by multiple match arms. Especially if the Msg variant contains Result or Option. It eliminates nesting and boilerplate in arm bodies. Example:
enum Msg {
Fetched(fetch::Result<MyData>)
}
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
match msg {
Msg::Fetched(Ok(response_data)) => {
model.response_data = Some(response_data);
},
Msg::Fetched(Err(fetch_error)) => {
error!("Cannot fetch data:", fetch_error);
}
}
}
Msg variants, split them visually into groups and divide them by comments to improve scannability. Example:enum Msg {
// ------ A ------
A1,
A2,
// ------ B ------
B1,
B2,
}
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
match msg {
// ------ A ------
Msg::A1 => { .. },
Msg::A2 => { .. },
// ------ B ------
Msg::B1 => { .. },
Msg::B2 => { .. },
}
}