yew/functional/hooks/use_force_update.rs
1use std::fmt;
2
3use super::{Hook, HookContext};
4use crate::functional::ReRender;
5
6/// A handle which can be used to force a re-render of the associated
7/// function component.
8#[derive(Clone)]
9pub struct UseForceUpdateHandle {
10 trigger: ReRender,
11}
12
13impl fmt::Debug for UseForceUpdateHandle {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 f.debug_struct("UseForceUpdate").finish()
16 }
17}
18
19impl UseForceUpdateHandle {
20 /// Trigger an unconditional re-render of the associated function component
21 pub fn force_update(&self) {
22 (self.trigger)()
23 }
24}
25
26#[cfg(nightly_yew)]
27mod feat_nightly {
28 use super::*;
29
30 impl FnOnce<()> for UseForceUpdateHandle {
31 type Output = ();
32
33 extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
34 self.force_update()
35 }
36 }
37
38 impl FnMut<()> for UseForceUpdateHandle {
39 extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
40 self.force_update()
41 }
42 }
43
44 impl Fn<()> for UseForceUpdateHandle {
45 extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
46 self.force_update()
47 }
48 }
49}
50
51/// This hook is used to manually force a function component to re-render.
52///
53/// # Note
54///
55/// Often, using this hook means that you're doing something wrong.
56/// Try to use more specialized hooks, such as [`use_state`] and [`use_reducer`].
57/// This hook should only be used when your component depends on external state where you
58/// can't subscribe to changes, or as a low-level primitive to enable such a subscription-based
59/// approach.
60///
61/// # Use-case
62///
63/// Use this hook when wrapping an API that doesn't expose precise subscription events for fetched
64/// data. You could then, at some point, invalidate your local cache of the fetched data and trigger
65/// a re-render to let the normal render flow of components tell you again which data to fetch, and
66/// repopulate the cache accordingly.
67///
68/// A large externally managed cache, such as a app-wide cache for GraphQL data
69/// should not rerender every component whenever new data arrives, but only those where a query
70/// changed.
71///
72/// If the state of your component is not shared, you should need to use this hook.
73///
74/// # Example
75///
76/// This example implements a silly, manually updated display of the current time. The component
77/// is re-rendered every time the button is clicked. You should usually use a timeout and
78/// `use_state` to automatically trigger a re-render every second without having to use this hook.
79///
80/// ```rust
81/// use yew::prelude::*;
82///
83/// #[function_component]
84/// fn ManuallyUpdatedDate() -> Html {
85/// let trigger = use_force_update();
86/// let onclick = use_state(move || Callback::from(move |_| trigger.force_update()));
87/// let last_update = js_sys::Date::new_0().to_utc_string();
88/// html! {
89/// <div>
90/// <button onclick={&*onclick}>{"Update now!"}</button>
91/// <p>{"Last updated: "}{last_update}</p>
92/// </div>
93/// }
94/// }
95/// ```
96///
97/// [`use_state`]: super::use_state()
98/// [`use_reducer`]: super::use_reducer()
99pub fn use_force_update() -> impl Hook<Output = UseForceUpdateHandle> {
100 struct UseRerenderHook;
101
102 impl Hook for UseRerenderHook {
103 type Output = UseForceUpdateHandle;
104
105 fn run(self, ctx: &mut HookContext) -> Self::Output {
106 UseForceUpdateHandle {
107 trigger: ctx.re_render.clone(),
108 }
109 }
110 }
111
112 UseRerenderHook
113}
114
115#[cfg(all(test, nightly_yew))]
116mod nightly_test {
117 use yew::prelude::*;
118
119 #[function_component]
120 fn ManuallyUpdatedDate() -> Html {
121 let trigger = use_force_update();
122 let _ = move || trigger();
123 html! {}
124 }
125}