diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs index 445506aa0..f7b0a51db 100644 --- a/packages/core-macro/src/props/mod.rs +++ b/packages/core-macro/src/props/mod.rs @@ -697,10 +697,14 @@ mod struct_info { let name = field.name; if optional { quote! { - // If the event handler is None, we don't need to update it + // If both event handler are Some, update them in place if let (Some(old_handler), Some(new_handler)) = (self.#name.as_mut(), new.#name.as_ref()) { old_handler.__point_to(new_handler); } + // Otherwise just move the new handler into self + else { + self.#name = new.#name; + } } } else { quote! { diff --git a/packages/core-macro/tests/values_memoize_in_place.rs b/packages/core-macro/tests/values_memoize_in_place.rs index 94d94d044..590654afe 100644 --- a/packages/core-macro/tests/values_memoize_in_place.rs +++ b/packages/core-macro/tests/values_memoize_in_place.rs @@ -215,3 +215,28 @@ fn cloning_read_signal_components_work() { } dom.render_immediate(&mut dioxus_core::NoOpMutations); } + +// Regression test for https://github.com/DioxusLabs/dioxus/issues/5222 +#[tokio::test] +async fn optional_event_handler_diff() { + use dioxus_core::Properties; + + #[derive(Props, Clone, PartialEq)] + struct CompProps { + callback: Option, + } + + let dom = VirtualDom::new(|| rsx! {}); + + dom.in_scope(ScopeId::APP, || { + // Diffing from None to Some should be different and copy the callback + let mut props = CompProps::builder().callback(None).build(); + assert!(!props.memoize(&CompProps::builder().callback(|_| {}).build())); + assert!(props.inner.callback.is_some()); + + // Diffing from Some to None should be different and remove the callback + let mut props = CompProps::builder().callback(|_| {}).build(); + assert!(!props.memoize(&CompProps::builder().callback(None).build())); + assert!(props.inner.callback.is_none()); + }); +}