diff --git a/examples/README.md b/examples/README.md index e9735ebd199..58da019a898 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,13 +3,8 @@ ## How to Run The examples are built with [trunk](https://github.com/thedodd/trunk). -You can install it with the following command: - -```bash -cargo install --locked trunk -``` - -Running an example is as easy as running a single command: +Once you have the development envrionment fully set up (see [documentation](https://yew.rs/docs/next/getting-started/introduction)), +running an example is as easy as running a single command: ```bash # move into the directory of the example you want to run diff --git a/examples/suspense/index.scss b/examples/suspense/index.scss index 5e3a5385e6a..22bc367b1de 100644 --- a/examples/suspense/index.scss +++ b/examples/suspense/index.scss @@ -10,10 +10,10 @@ html, body { .layout { height: 100vh; width: 100%; - display: flex; - flex-direction: column; - justify-content: center; + display: grid; + justify-items: center; align-items: center; + grid-template-columns: 1fr 1fr; } .content { @@ -69,3 +69,7 @@ button { color: rgb(100, 100, 100); } + +h2 { + text-align: center; +} diff --git a/examples/suspense/src/main.rs b/examples/suspense/src/main.rs index a51d99266f2..09b8d5e24f6 100644 --- a/examples/suspense/src/main.rs +++ b/examples/suspense/src/main.rs @@ -1,13 +1,19 @@ use web_sys::HtmlTextAreaElement; use yew::prelude::*; +mod struct_consumer; mod use_sleep; -use use_sleep::use_sleep; +pub use use_sleep::use_sleep; + +#[derive(Debug, PartialEq, Properties)] +struct PleaseWaitProps { + from: &'static str, +} #[function_component(PleaseWait)] -fn please_wait() -> Html { - html! {
{"Please wait 5 Seconds..."}
} +fn please_wait(props: &PleaseWaitProps) -> Html { + html! {
{"Please wait 5 Seconds for "}{props.from}{" component to load..."}
} } #[function_component(AppContent)] @@ -20,7 +26,7 @@ fn app_content() -> HtmlResult { let value = value.clone(); Callback::from(move |e: InputEvent| { - let input: HtmlTextAreaElement = e.target_unchecked_into(); + let input: HtmlTextAreaElement = e.target_unchecked_into::(); value.set(input.value()); }) @@ -41,14 +47,21 @@ fn app_content() -> HtmlResult { #[function_component(App)] fn app() -> Html { - let fallback = html! {}; + let fallback_fn = html! {}; + let fallback_struct = html! {}; html! {
-

{"Yew Suspense Demo"}

- - +

{" Yew Suspense Demo -- function component consumer"}

+ + + +
+
+

{"Yew Suspense Demo -- struct component consumer"}

+ +
diff --git a/examples/suspense/src/struct_consumer.rs b/examples/suspense/src/struct_consumer.rs new file mode 100644 index 00000000000..3bda239320d --- /dev/null +++ b/examples/suspense/src/struct_consumer.rs @@ -0,0 +1,70 @@ +use web_sys::HtmlTextAreaElement; +use yew::prelude::*; + +use crate::use_sleep; + +#[function_component] +pub fn WithSleep() -> HtmlResult +where + Comp: BaseComponent, +{ + let sleep = use_sleep()?; + let sleep = Callback::from(move |_| sleep()); + Ok(yew::virtual_dom::VChild::::new(AppContentProps { resleep: sleep }, None).into()) +} + +#[derive(Debug, PartialEq, Properties)] +pub struct AppContentProps { + pub resleep: Callback<()>, +} + +pub type AppContent = WithSleep; + +pub enum Msg { + ValueUpdate(String), + TakeABreak, +} + +pub struct BaseAppContent { + value: String, +} + +impl Component for BaseAppContent { + type Message = Msg; + type Properties = AppContentProps; + + fn create(_ctx: &Context) -> Self { + Self { + value: "I am writing a long story...".to_string(), + } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::ValueUpdate(v) => { + self.value = v; + } + Msg::TakeABreak => { + ctx.props().resleep.emit(()); + } + }; + true + } + + fn view(&self, ctx: &Context) -> Html { + let oninput = ctx.link().callback(|e: InputEvent| { + let input: HtmlTextAreaElement = e.target_unchecked_into::(); + Msg::ValueUpdate(input.value()) + }); + let on_take_a_break = ctx.link().callback(|_| Msg::TakeABreak); + html! { +
+ +
+ +
{"You can take a break at anytime"}
{"and your work will be preserved."}
+
+
+ } + } +} diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index c1ef4e72c6a..19d814ed4be 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -641,6 +641,9 @@ impl Parse for HtmlElementOpen { if let Some(attr) = props.value.take() { props.attributes.push(attr); } + if let Some(attr) = props.checked.take() { + props.attributes.push(attr); + } } } } diff --git a/packages/yew-router/src/components/link.rs b/packages/yew-router/src/components/link.rs index cf94c92b18c..d0853d64792 100644 --- a/packages/yew-router/src/components/link.rs +++ b/packages/yew-router/src/components/link.rs @@ -9,10 +9,11 @@ use crate::{utils, Routable}; /// Props for [`Link`] #[derive(Properties, Clone, PartialEq)] -pub struct LinkProps +pub struct LinkProps where R: Routable, Q: Clone + PartialEq + Serialize, + S: Clone + PartialEq, { /// CSS classes to add to the anchor element (optional). #[prop_or_default] @@ -22,6 +23,9 @@ where /// Route query data #[prop_or_default] pub query: Option, + /// Route state data + #[prop_or_default] + pub state: Option, #[prop_or_default] pub disabled: bool, /// [`NodeRef`](yew::html::NodeRef) for the `` element. @@ -33,18 +37,20 @@ where /// A wrapper around `` tag to be used with [`Router`](crate::Router) #[function_component] -pub fn Link(props: &LinkProps) -> Html +pub fn Link(props: &LinkProps) -> Html where R: Routable + 'static, Q: Clone + PartialEq + Serialize + 'static, + S: Clone + PartialEq + 'static, { let LinkProps { classes, to, - children, - disabled, query, + state, + disabled, anchor_ref, + children, } = props.clone(); let navigator = use_navigator().expect_throw("failed to get navigator"); @@ -53,21 +59,30 @@ where let navigator = navigator.clone(); let to = to.clone(); let query = query.clone(); + let state = state.clone(); Callback::from(move |e: MouseEvent| { if e.meta_key() || e.ctrl_key() || e.shift_key() || e.alt_key() { return; } e.prevent_default(); - match query { - None => { + match (&state, &query) { + (None, None) => { navigator.push(&to); } - Some(ref data) => { + (Some(state), None) => { + navigator.push_with_state(&to, state.clone()); + } + (None, Some(query)) => { navigator - .push_with_query(&to, data) + .push_with_query(&to, query) .expect_throw("failed push history with query"); } + (Some(state), Some(query)) => { + navigator + .push_with_query_and_state(&to, query, state.clone()) + .expect_throw("failed push history with query and state"); + } } }) }; diff --git a/packages/yew/src/dom_bundle/btag/mod.rs b/packages/yew/src/dom_bundle/btag/mod.rs index fca3b521344..c590d6be6c5 100644 --- a/packages/yew/src/dom_bundle/btag/mod.rs +++ b/packages/yew/src/dom_bundle/btag/mod.rs @@ -1096,6 +1096,7 @@ mod layout_tests { #[cfg(test)] mod tests_without_browser { use crate::html; + use crate::virtual_dom::VNode; #[test] fn html_if_bool() { @@ -1268,4 +1269,32 @@ mod tests_without_browser { html! {
<>
}, ); } + + #[test] + fn input_checked_stays_there() { + let tag = html! { + + }; + match tag { + VNode::VTag(tag) => { + assert_eq!(tag.checked(), Some(true)); + } + _ => unreachable!(), + } + } + #[test] + fn non_input_checked_stays_there() { + let tag = html! { + + }; + match tag { + VNode::VTag(tag) => { + assert_eq!( + tag.attributes.iter().find(|(k, _)| *k == "checked"), + Some(("checked", "true")) + ); + } + _ => unreachable!(), + } + } } diff --git a/packages/yew/src/suspense/hooks.rs b/packages/yew/src/suspense/hooks.rs index 04a5e51c722..b31a57663d6 100644 --- a/packages/yew/src/suspense/hooks.rs +++ b/packages/yew/src/suspense/hooks.rs @@ -72,7 +72,7 @@ where T: Future + 'static, O: 'static, { - use_future_with_deps(move |_| init_f(), ()) + use_future_with((), move |_| init_f()) } /// Use the result of an async computation with dependencies, suspending while waiting. @@ -84,7 +84,7 @@ where /// /// [ready]: std::task::Poll::Ready #[hook] -pub fn use_future_with_deps(f: F, deps: D) -> SuspensionResult> +pub fn use_future_with(deps: D, f: F) -> SuspensionResult> where F: FnOnce(Rc) -> T, T: Future + 'static, diff --git a/packages/yew/tests/suspense.rs b/packages/yew/tests/suspense.rs index cc3f0bf7e10..0596f3c6946 100644 --- a/packages/yew/tests/suspense.rs +++ b/packages/yew/tests/suspense.rs @@ -13,7 +13,7 @@ use web_sys::{HtmlElement, HtmlTextAreaElement}; use yew::platform::spawn_local; use yew::platform::time::sleep; use yew::prelude::*; -use yew::suspense::{use_future, use_future_with_deps, Suspension, SuspensionResult}; +use yew::suspense::{use_future, use_future_with, Suspension, SuspensionResult}; use yew::UseStateHandle; wasm_bindgen_test_configure!(run_in_browser); @@ -645,13 +645,10 @@ async fn use_suspending_future_with_deps_works() { #[function_component(Content)] fn content(ContentProps { delay_millis }: &ContentProps) -> HtmlResult { - let delayed_result = use_future_with_deps( - |delay_millis| async move { - sleep(Duration::from_millis(*delay_millis)).await; - 42 - }, - *delay_millis, - )?; + let delayed_result = use_future_with(*delay_millis, |delay_millis| async move { + sleep(Duration::from_millis(*delay_millis)).await; + 42 + })?; Ok(html! {
diff --git a/tools/changelog/src/cli.rs b/tools/changelog/src/cli.rs index 4d1e434d3f7..d15ba731c6a 100644 --- a/tools/changelog/src/cli.rs +++ b/tools/changelog/src/cli.rs @@ -81,25 +81,38 @@ impl Cli { let log_lines = create_log_lines(from_ref, to, package_labels, token)?; // categorize logs - let (fixes, features): (Vec<_>, Vec<_>) = log_lines + let (breaking_changes, filtered_log_lines): (Vec<_>, Vec<_>) = log_lines .into_iter() - .partition(|log_line| log_line.message.to_lowercase().contains("fix")); + .partition(|log_line| log_line.is_breaking_change); + + let (fixes, features): (Vec<_>, Vec<_>) = + filtered_log_lines + .into_iter() + .partition(|filtered_log_line| { + filtered_log_line.message.to_lowercase().contains("fix") + }); // create displayable log lines let fixes_logs = write_log_lines(fixes)?; let features_logs = write_log_lines(features)?; + let breaking_changes_logs = write_log_lines(breaking_changes)?; if !skip_file_write { // create version changelog - let version_changelog = - write_changelog_file(&fixes_logs, &features_logs, package, next_version)?; + let version_changelog = write_changelog_file( + &fixes_logs, + &features_logs, + &breaking_changes_logs, + package, + next_version, + )?; // write changelog write_changelog(&changelog_path, &version_changelog)?; } // stdout changelog meant for tag description - stdout_tag_description_changelog(&fixes_logs, &features_logs)?; + stdout_tag_description_changelog(&fixes_logs, &features_logs, &breaking_changes_logs)?; Ok(()) } diff --git a/tools/changelog/src/create_log_line.rs b/tools/changelog/src/create_log_line.rs index 29afafaf54c..f0abb64e0c9 100644 --- a/tools/changelog/src/create_log_line.rs +++ b/tools/changelog/src/create_log_line.rs @@ -89,11 +89,15 @@ pub fn create_log_line( } return Ok(None); } + let is_breaking_change = issue_labels + .iter() + .any(|label| label.to_lowercase().contains("breaking change")); let log_line = LogLine { message, user: author_name.to_string(), issue_id, + is_breaking_change, }; println!("{log_line:?}"); diff --git a/tools/changelog/src/log_line.rs b/tools/changelog/src/log_line.rs index e8d5596fd7c..b5a4d4af8ba 100644 --- a/tools/changelog/src/log_line.rs +++ b/tools/changelog/src/log_line.rs @@ -3,4 +3,5 @@ pub struct LogLine { pub message: String, pub user: String, pub issue_id: String, + pub is_breaking_change: bool, } diff --git a/tools/changelog/src/stdout_tag_description_changelog.rs b/tools/changelog/src/stdout_tag_description_changelog.rs index adbb7ab0739..13b6821dec6 100644 --- a/tools/changelog/src/stdout_tag_description_changelog.rs +++ b/tools/changelog/src/stdout_tag_description_changelog.rs @@ -2,13 +2,17 @@ use std::io::{stdout, Write}; use anyhow::Result; -pub fn stdout_tag_description_changelog(fixes_logs: &[u8], features_logs: &[u8]) -> Result<()> { +pub fn stdout_tag_description_changelog( + fixes_logs: &[u8], + features_logs: &[u8], + breaking_changes_logs: &[u8], +) -> Result<()> { let mut tag_changelog = Vec::new(); writeln!(tag_changelog, "# Changelog")?; writeln!(tag_changelog)?; - if fixes_logs.is_empty() && features_logs.is_empty() { + if fixes_logs.is_empty() && features_logs.is_empty() && breaking_changes_logs.is_empty() { writeln!(tag_changelog, "No changes")?; writeln!(tag_changelog)?; } @@ -26,6 +30,12 @@ pub fn stdout_tag_description_changelog(fixes_logs: &[u8], features_logs: &[u8]) tag_changelog.extend(features_logs); } + if !breaking_changes_logs.is_empty() { + writeln!(tag_changelog, "## 🚨 Breaking changes")?; + writeln!(tag_changelog)?; + tag_changelog.extend(breaking_changes_logs); + } + stdout().write_all(&tag_changelog)?; Ok(()) diff --git a/tools/changelog/src/write_changelog_file.rs b/tools/changelog/src/write_changelog_file.rs index 4eb95d99943..976d5a65cfb 100644 --- a/tools/changelog/src/write_changelog_file.rs +++ b/tools/changelog/src/write_changelog_file.rs @@ -9,7 +9,7 @@ pub fn write_changelog(changelog_path: &str, version_changelog: &[u8]) -> Result .context(format!("could not open {changelog_path} for reading"))?; let old_changelog_reader = BufReader::new(old_changelog); - let changelog_path_new = &format!("../{changelog_path}.new"); + let changelog_path_new = &format!("{changelog_path}.new"); let mut new_changelog = fs::OpenOptions::new() .write(true) diff --git a/tools/changelog/src/write_log_lines.rs b/tools/changelog/src/write_log_lines.rs index 152dcf0735d..29d6387902d 100644 --- a/tools/changelog/src/write_log_lines.rs +++ b/tools/changelog/src/write_log_lines.rs @@ -10,6 +10,7 @@ pub fn write_log_lines(log_lines: Vec) -> Result> { message, user, issue_id, + .. } in log_lines { writeln!( diff --git a/tools/changelog/src/write_version_changelog.rs b/tools/changelog/src/write_version_changelog.rs index a5285d75807..806e6f7c384 100644 --- a/tools/changelog/src/write_version_changelog.rs +++ b/tools/changelog/src/write_version_changelog.rs @@ -8,6 +8,7 @@ use crate::yew_package::YewPackage; pub fn write_changelog_file( fixes_logs: &[u8], features_logs: &[u8], + breaking_changes_logs: &[u8], package: YewPackage, next_version: Version, ) -> Result> { @@ -25,7 +26,7 @@ pub fn write_changelog_file( )?; writeln!(version_only_changelog)?; - if fixes_logs.is_empty() && features_logs.is_empty() { + if fixes_logs.is_empty() && features_logs.is_empty() && breaking_changes_logs.is_empty() { writeln!(version_only_changelog, "No changes")?; writeln!(version_only_changelog)?; } @@ -44,5 +45,12 @@ pub fn write_changelog_file( writeln!(version_only_changelog)?; } + if !breaking_changes_logs.is_empty() { + writeln!(version_only_changelog, "### 🚨 Breaking changes")?; + writeln!(version_only_changelog)?; + version_only_changelog.extend(breaking_changes_logs); + writeln!(version_only_changelog)?; + } + Ok(version_only_changelog) } diff --git a/tools/changelog/tests/generate_yew_changelog_file.rs b/tools/changelog/tests/generate_yew_changelog_file.rs index c4782cd1901..56a80cb4f0b 100644 --- a/tools/changelog/tests/generate_yew_changelog_file.rs +++ b/tools/changelog/tests/generate_yew_changelog_file.rs @@ -17,19 +17,12 @@ impl Drop for FileDeleteOnDrop { } } -#[test] -fn generate_yew_changelog_file() -> Result<()> { - // Setup - let file_delete_on_drop = FileDeleteOnDrop; - - fs::copy("tests/test_base.md", "tests/test_changelog.md")?; - - // Run +fn _generate_yew_changelog_file(from: &str, to: &str) -> Result<()> { let cli_args = Cli { package: YewPackage::from_str("yew").unwrap(), new_version_level: NewVersionLevel::Minor, - from: Some("abeb8bc3f1ffabc8a58bd9ba4430cd091a06335a".to_string()), - to: "d8ec50150ed27e2835bb1def26d2371a8c2ab750".to_string(), + from: Some(from.to_string()), + to: to.to_string(), changelog_path: "tests/test_changelog.md".to_string(), skip_file_write: false, skip_get_bump_version: true, @@ -38,6 +31,27 @@ fn generate_yew_changelog_file() -> Result<()> { cli_args.run().unwrap(); + Ok(()) +} + +#[test] +fn generate_yew_changelog_file() -> Result<()> { + // Setup + let file_delete_on_drop = FileDeleteOnDrop; + + fs::copy("tests/test_base.md", "tests/test_changelog.md")?; + + // Run + _generate_yew_changelog_file( + "abeb8bc3f1ffabc8a58bd9ba4430cd091a06335a", + "d8ec50150ed27e2835bb1def26d2371a8c2ab750", + )?; + + _generate_yew_changelog_file( + "8086a73a217a099a46138f4363411827b18d1cb0", + "934aedbc8815fd77fc6630b644cfea4f9a071236", + )?; + // Check let expected = File::open("tests/test_expected.md")?; let expected_reader_lines = BufReader::new(expected).lines(); @@ -48,13 +62,13 @@ fn generate_yew_changelog_file() -> Result<()> { let lines = expected_reader_lines.zip(after_reader_lines); for (i, (expected_line, after_line)) in lines.enumerate() { - if i == 2 { - // third line has dynamic things that may break the tests - let expected_third_line = expected_line?.replace( + if i == 2 || i == 13 { + // these lines have dynamic things that may break the tests + let expected_line_updated = expected_line?.replace( "date_goes_here", Utc::now().format("%Y-%m-%d").to_string().as_str(), ); - assert_eq!(expected_third_line, after_line?); + assert_eq!(expected_line_updated, after_line?); } else { assert_eq!(expected_line?, after_line?); } diff --git a/tools/changelog/tests/test_expected.md b/tools/changelog/tests/test_expected.md index 09d8f8f92e7..5e186956986 100644 --- a/tools/changelog/tests/test_expected.md +++ b/tools/changelog/tests/test_expected.md @@ -2,9 +2,20 @@ ## ✨ yew **0.0.0** *(date_goes_here)* Changelog +### ⚡️ Features + +- Incremental performance improvements to element creation. [[@Greg Johnston](https://github.com/Greg Johnston), [#3169](https://github.com/yewstack/yew/pull/3169)] + +### 🚨 Breaking changes + +- Enable PartialEq for all virtual dom types. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#3206](https://github.com/yewstack/yew/pull/3206)] +- Pass hook dependencies as the first function argument. [[@Arniu Tseng](https://github.com/Arniu Tseng), [#2861](https://github.com/yewstack/yew/pull/2861)] + +## ✨ yew **0.0.0** *(date_goes_here)* Changelog + ### 🛠 Fixes -- Fix defaulted type parameter.. [[@futursolo](https://github.com/futursolo), [#2284](https://github.com/yewstack/yew/pull/2284)] +- Fix defaulted type parameter.. [[@Kaede Hoshikawa](https://github.com/Kaede Hoshikawa), [#2284](https://github.com/yewstack/yew/pull/2284)] ### ⚡️ Features diff --git a/website/docs/advanced-topics/server-side-rendering.md b/website/docs/advanced-topics/server-side-rendering.md index 8fef136efda..6d3789ff33c 100644 --- a/website/docs/advanced-topics/server-side-rendering.md +++ b/website/docs/advanced-topics/server-side-rendering.md @@ -147,6 +147,15 @@ one implementation, you may want to use a `PhantomComponent` to fill the position of the extra component. ::: +:::warning + +The hydration can only succeed if the real DOM matches the expected DOM +after initial render of the SSR output (static HTML) by browser. If your HTML is +not spec-compliant, the hydration _may_ fail. Browsers may change the DOM structure +of the incorrect HTML, causing the actual DOM to be different from the expected DOM. +For example, [if you have a `` without a ``, the browser may add a `` to the DOM](https://github.com/yewstack/yew/issues/2684) +::: + ## Component Lifecycle during hydration During Hydration, components schedule 2 consecutive renders after it is diff --git a/website/docs/concepts/suspense.mdx b/website/docs/concepts/suspense.mdx index 74d6dff1ddd..6c697d82f00 100644 --- a/website/docs/concepts/suspense.mdx +++ b/website/docs/concepts/suspense.mdx @@ -136,45 +136,11 @@ fn app() -> Html { ### Use Suspense in Struct Components It's not possible to suspend a struct component directly. However, you -can use a function component as a [HOC](../advanced-topics/struct-components/hoc) to -achieve suspense-based data fetching. +can use a function component as a [Higher Order Component](../advanced-topics/struct-components/hoc) +to achieve suspense-based data fetching. -```rust ,ignore -use yew::prelude::*; - -#[function_component(WithUser)] -fn with_user() -> HtmlResult -where T: BaseComponent -{ - let user = use_user()?; - - Ok(html! {}) -} - -#[derive(Debug, PartialEq, Properties)] -pub struct UserContentProps { - pub user: User, -} - -pub struct BaseUserContent; - -impl Component for BaseUserContent { - type Properties = UserContentProps; - type Message = (); - - fn create(ctx: &Context) -> Self { - Self - } - - fn view(&self, ctx: &Context) -> Html { - let name = ctx.props().user.name; - - html! {
{"Hello, "}{name}{"!"}
} - } -} - -pub type UserContent = WithUser; -``` +The [suspense example in the Yew repository](https://github.com/yewstack/yew/tree/master/examples/suspense/src/struct_consumer.rs) +demonstrates how to use. ## Relevant examples diff --git a/website/docs/migration-guides/yew/from-0_20_0-to-next.mdx b/website/docs/migration-guides/yew/from-0_20_0-to-next.mdx index e1f78cce413..9fbc98ce013 100644 --- a/website/docs/migration-guides/yew/from-0_20_0-to-next.mdx +++ b/website/docs/migration-guides/yew/from-0_20_0-to-next.mdx @@ -25,11 +25,14 @@ sg --pattern 'use_callback($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_callback( sg --pattern 'use_memo($CALLBACK,$$$DEPENDENCIES)' --rewrite 'use_memo($$$DEPENDENCIES, $CALLBACK)' -l rs -i sg --pattern 'use_memo($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_memo($DEPENDENCIES,$$$CALLBACK)' -l rs -i -sg --pattern 'use_transitive_state($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_transitive_state($DEPENDENCIES,$$$CALLBACK)' -l rs -i -sg --pattern 'use_transitive_state($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_transitive_state($DEPENDENCIES,$$$CALLBACK)' -l rs -i +sg --pattern 'use_future_with_deps($CALLBACK,$$$DEPENDENCIES)' --rewrite 'use_effect_with($$$DEPENDENCIES, $CALLBACK)' -l rs -i +sg --pattern 'use_future_with($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_effect_with($DEPENDENCIES,$$$CALLBACK)' -l rs -i -sg --pattern 'use_prepared_state($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_prepared_state($DEPENDENCIES,$$$CALLBACK)' -l rs -i -sg --pattern 'use_prepared_state($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_prepared_state($DEPENDENCIES,$$$CALLBACK)' -l rs -i +sg --pattern 'use_transitive_state!($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_transitive_state!($DEPENDENCIES,$$$CALLBACK)' -l rs -i +sg --pattern 'use_transitive_state!($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_transitive_state!($DEPENDENCIES,$$$CALLBACK)' -l rs -i + +sg --pattern 'use_prepared_state!($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_prepared_state!($DEPENDENCIES,$$$CALLBACK)' -l rs -i +sg --pattern 'use_prepared_state!($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_prepared_state!($DEPENDENCIES,$$$CALLBACK)' -l rs -i ``` ### Reasoning