Introduce a dedicated use_force_update hook (#2586)

* introduce a dedicated use_force_update hook

* fix doc links
This commit is contained in:
WorldSEnder 2022-04-09 13:54:39 +02:00 committed by GitHub
parent ae26885589
commit 6992a454e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 0 deletions

View File

@ -1,6 +1,7 @@
mod use_callback;
mod use_context;
mod use_effect;
mod use_force_update;
mod use_memo;
mod use_reducer;
mod use_ref;
@ -9,6 +10,7 @@ mod use_state;
pub use use_callback::*;
pub use use_context::*;
pub use use_effect::*;
pub use use_force_update::*;
pub use use_memo::*;
pub use use_reducer::*;
pub use use_ref::*;

View File

@ -0,0 +1,84 @@
use super::{Hook, HookContext};
use crate::functional::ReRender;
use std::fmt;
/// A handle which can be used to force a re-render of the associated
/// function component.
#[derive(Clone)]
pub struct UseForceUpdate {
trigger: ReRender,
}
impl fmt::Debug for UseForceUpdate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UseForceUpdate").finish()
}
}
impl UseForceUpdate {
/// Trigger an unconditional re-render of the associated function component
pub fn force_update(&self) {
(self.trigger)()
}
}
// #![feature(fn_traits)] // required nightly feature to make UseForceUpdate callable directly
// impl Fn<()> for UseForceUpdate {
// extern "rust-call" fn call(&self, _args: ()) {
// self.force_update()
// }
// }
/// This hook is used to manually force a function component to re-render.
///
/// Try to use more specialized hooks, such as [`use_state`] and [`use_reducer`].
/// This hook should only be used when your component depends on external state where you
/// can't subscribe to changes, or as a low-level primitive to enable such a subscription-based
/// approach.
///
/// For example, a large externally managed cache, such as a app-wide cache for GraphQL data
/// should not rerender every component whenever new data arrives, but only those where a query
/// changed.
///
/// If the state of your component is not shared, you should need to use this hook.
///
/// # Example
///
/// This example implements a silly, manually updated display of the current time. The component
/// is rerendered every time the button is clicked. You should usually use a timeout and `use_state`
/// to automatically trigger a re-render every second without having to use this hook.
///
/// ```rust
/// # use yew::prelude::*;
///
/// #[function_component]
/// fn ManuallyUpdatedDate() -> Html {
/// let trigger = use_force_update();
/// let onclick = use_state(move || Callback::from(move |_| trigger.force_update()));
/// let last_update = js_sys::Date::new_0().to_utc_string();
/// html! {
/// <div>
/// <button onclick={&*onclick}>{"Update now!"}</button>
/// <p>{"Last updated: "}{last_update}</p>
/// </div>
/// }
/// }
/// ```
///
/// [`use_state`]: super::use_state()
/// [`use_reducer`]: super::use_reducer()
pub fn use_force_update() -> impl Hook<Output = UseForceUpdate> {
struct UseRerenderHook;
impl Hook for UseRerenderHook {
type Output = UseForceUpdate;
fn run(self, ctx: &mut HookContext) -> Self::Output {
UseForceUpdate {
trigger: ctx.re_render.clone(),
}
}
}
UseRerenderHook
}

View File

@ -36,6 +36,7 @@ Yew comes with the following predefined Hooks:
- [`use_effect`](./use-effect.mdx)
- [`use_effect_with_deps`](./use-effect.mdx#use_effect_with_deps)
- [`use_context`](./use-context.mdx)
- [`use_force_update`](./use-force-update)
### Custom Hooks

View File

@ -0,0 +1,19 @@
---
title: 'use_force_update'
---
`use_force_update` is a low-level hook specialized to triggering a re-render of a function component.
Usually, this re-render is triggered automatically by the hook holding your data, for example when
changing the data in a handle returned from [`use_state`](./use-state).
::: caution
Often, using this hook means you are doing something wrong and should use one of the other hooks,
for example [`use_reducer`](./use-reducer) is a great way to track changing data.
:::
Use this hook when wrapping an API that doesn't expose precise subscription events for fetched data.
You could then, at some point, invalidate your local cache of the fetched data and trigger a re-render
to let the normal render flow of components tell you again which data to fetch, and repopulate the
cache accordingly.