Custom Hooks

Defining custom Hooks

Component’s stateful logic can be extracted into usable function by creating custom Hooks.

Consider that we have a component which subscribes to an agent and displays the messages sent to it.

  1. use yew::{function_component, html, use_effect, use_state, Callback};
  2. use yew_agent::Bridged;
  3. // EventBus is an implementation yew_agent::Agent
  4. use website_test::agents::EventBus;
  5. #[function_component(ShowMessages)]
  6. pub fn show_messages() -> Html {
  7. let state = use_state(Vec::new);
  8. {
  9. let state = state.clone();
  10. use_effect(move || {
  11. let producer = EventBus::bridge(Callback::from(move |msg| {
  12. let mut messages = (*state).clone();
  13. messages.push(msg);
  14. state.set(messages)
  15. }));
  16. || drop(producer)
  17. });
  18. }
  19. let output = state.iter().map(|it| html! { <p>{ it }</p> });
  20. html! { <div>{ for output }</div> }
  21. }

There’s one problem with this code: the logic can’t be reused by another component. If we build another component which keeps track of the messages, instead of copying the code we can move the logic into a custom hook.

We’ll start by creating a new function called use_subscribe. The use_ prefix conventionally denotes that a function is a hook. This function will take no arguments and return Rc<RefCell<Vec<String>>>.

  1. use std::{cell::RefCell, rc::Rc};
  2. fn use_subscribe() -> Rc<RefCell<Vec<String>>> {
  3. todo!()
  4. }

This is a simple hook which can be created by combining other hooks. For this example, we’ll two pre-defined hooks. We’ll use use_state hook to store the Vec for messages, so they persist between component re-renders. We’ll also use use_effect to subscribe to the EventBus Agent so the subscription can be tied to component’s lifecycle.

  1. use std::collections::HashSet;
  2. use yew::{use_effect, use_state, Callback};
  3. use yew_agent::Bridged;
  4. // EventBus is an implementation yew_agent::Agent
  5. use website_test::agents::EventBus;
  6. fn use_subscribe() -> Vec<String> {
  7. let state = use_state(Vec::new);
  8. let effect_state = state.clone();
  9. use_effect(move || {
  10. let producer = EventBus::bridge(Callback::from(move |msg| {
  11. let mut messages = (*effect_state).clone();
  12. messages.push(msg);
  13. effect_state.set(messages)
  14. }));
  15. || drop(producer)
  16. });
  17. (*state).clone()
  18. }

Although this approach works in almost all cases, it can’t be used to write primitive hooks like the pre-defined hooks we’ve been using already

Writing primitive hooks

use_hook function is used to write such hooks. View the docs on docs.rs for the documentation and hooks directory to see implementations of pre-defined hooks.