Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a dedicated use_force_update hook #2586

Merged
merged 2 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/yew/src/functional/hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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::*;
Expand Down
84 changes: 84 additions & 0 deletions packages/yew/src/functional/hooks/use_force_update.rs
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we follow UseXXXHandle convention and rename it to UseForceUpdateHandle? like UseReducerHandle / UseStateHandle etc

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()
// }
// }
Comment on lines +25 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should put this behind a nightly feature flag but that can be done in a separate PR


/// 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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.