Skip to content

Commit

Permalink
documentation for ComponentRef
Browse files Browse the repository at this point in the history
  • Loading branch information
WorldSEnder committed Mar 27, 2022
1 parent 309b93d commit 9fd3f67
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 38 deletions.
9 changes: 8 additions & 1 deletion packages/yew/src/functional/hooks/use_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc;

use crate::functional::{hook, use_memo, use_state};
use crate::NodeRef;
use crate::{Component, ComponentRef, NodeRef};

/// This hook is used for obtaining a mutable reference to a stateful value.
/// Its state persists across renders.
Expand Down Expand Up @@ -119,3 +119,10 @@ where
pub fn use_node_ref() -> NodeRef {
(*use_state(NodeRef::default)).clone()
}

/// This hook is used for obtaining a [`ComponentRef`].
/// The reference persists across renders, but a rerender is not triggered if the underlying ref changes.
#[hook]
pub fn use_component_ref<COMP: Component>() -> ComponentRef<COMP> {
(*use_state(ComponentRef::default)).clone()
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ much stricter. It also provides super-powers like conditional rendering and rend

## Passing data to a component

Yew components use _props_ to communicate between parent and children. A parent component may pass any data as props to
Yew components use _props_ to interact between parent and children. A parent component may pass any data as props to
its children. Props are similar to HTML attributes but any Rust type can be passed as props.

:::info
[Learn more about the props](./properties)
:::

:::info
For other than parent/child communication, use [contexts](./contexts)
To pass data to all descendants use [contexts](./contexts)
:::
8 changes: 4 additions & 4 deletions website/docs/advanced-topics/struct-components/lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ stages in the lifecycle of a component.

### Create

When a component is created, it receives properties from its parent component and is stored within
When a component is created, it receives properties from its parent component, stored within
the `Context<Self>` that's passed down to the `create` method. The properties can be used to
initialize the component's state and the "link" can be used to register callbacks or send messages to the component.

Expand Down Expand Up @@ -93,10 +93,10 @@ For usage details, check out [the `html!` guide](../html).
### Rendered

The `rendered` component lifecycle method is called once `view` has been called and Yew has rendered
the results to the DOM, but before the browser refreshes the page. This method is useful when you
the results to the DOM, but before control is yielded to the browser. This method is useful when you
want to perform actions that can only be completed after the component has rendered elements. There
is also a parameter called `first_render` which can be used to determine whether this function is
being called on the first render, or instead a subsequent one.
being called on the first render or a subsequent one.

```rust
use web_sys::HtmlInputElement;
Expand Down Expand Up @@ -145,7 +145,7 @@ Note that this lifecycle method does not require an implementation and will do n
Communication with components happens primarily through messages which are handled by the
`update` lifecycle method. This allows the component to update itself
based on what the message was, and determine if it needs to re-render itself. Messages can be sent
by event listeners, child components, Agents, Services, or Futures.
by event listeners, child and parent components, Agents, Services, or Futures.

Here's an example of what an implementation of `update` could look like:

Expand Down
88 changes: 62 additions & 26 deletions website/docs/advanced-topics/struct-components/refs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,88 @@ title: "Refs"
description: "Out-of-band DOM access"
---

The `ref` keyword can be used inside of any HTML element or component to get the DOM `Element` that
the item is attached to. This can be used to make changes to the DOM outside of the `view` lifecycle
method.
The `ref` keyword can be used on elements and struct components. See the page about [`NodeRef`](../../concepts/html/node-refs)
for its usage on elements. When used on a component, the ref does not bind a DOM `Element`. Instead,
the `ComponentRef` contains a reference to the `Scope` of the child, bound when the child is rendered.

This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.
For example, using a `NodeRef` in a component's `rendered` method allows you to make draw calls to
a canvas element after it has been rendered from `view`.
The `ComponentRef` can then be used to send messages to the attached component, which are handdled in the `update`
lifecycle method to perform side-effects or cause a rerender. Note that `ComponentRef<_>` is generically typed
and receives the type of the referenced component as argument.

The syntax is:
By capturing a reference to the `Scope`, instead of a specific element inside the ref-ed component, component implementations
are more modular. If you want to expose specific elements, for example the `<input>` element of a styled button, you should
add a property of type `NodeRef` and bind the ref you receive from your components' parent to an element in the `html!`.

A full example can be given by an externally controlled "light switch". Note that the state of the light
is contained inside the `Light` component, and the `Room` ties this together by sending a message to the
`Light` in the callback passed to the `Switch`.

```rust
use web_sys::Element;
use yew::{html, Component, Context, Html, NodeRef};
#[derive(PartialEq, Properties)]
struct SwitchProps {
on_toggle: Callback<MouseEvent>,
}

#[function_component]
fn Switch(SwitchProps { on_toggle }: &SwitchProps) -> Html {
html! {
<button onclick={on_toggle}>{"Switch"}</button>
}
}

enum LightMessage {
Toggle,
}

struct Comp {
node_ref: NodeRef,
struct Light {
is_on: bool,
}

impl Component for Comp {
type Message = ();
impl Component for Light {
type Properties = ();
type Message = LightMessage;

fn create(_ctx: &Context<Self>) -> Self {
Self {
// highlight-next-line
node_ref: NodeRef::default(),
}
Light { is_on: true }
}

fn update(&mut self, _ctx: &Context<Self>, LightMessage::Toggle: LightMessage) -> bool {
self.is_on = !self.is_on;
true
}

fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
// highlight-next-line
<div ref={self.node_ref.clone()}></div>
<div class={classes!["light", self.is_on.then(|| "on")]} />
}
}
}

fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
// highlight-start
let has_attributes = self.node_ref
.cast::<Element>()
.unwrap()
.has_attributes();
// highlight-end
#[function_component]
fn Room() -> Html {
// highlight-next-line
let light_ref: ComponentRef<Light> = use_component_ref();
let on_toggle = {
let light_ref = light_ref.clone();
Callback::from(move |_| {
// highlight-start
light_ref
.get()
.expect("a light to have rendered")
.send_message(LightMessage::Toggle)
// highlight-end
})
};
html! {
<>
// highlight-next-line
<Light ref={&light_ref} />
<Switch {on_toggle} />
</>
}
}
```

## Relevant examples
- [Node Refs](https://github.com/yewstack/yew/tree/master/examples/node_refs)
- [Nested List](https://github.com/yewstack/yew/tree/master/examples/nested_list)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: "use_node_ref"
---

`use_node_ref` is used for maintaining an easy reference to the DOM element represented as a `NodeRef`. It also persists across renders.
`use_node_ref` is used for maintaining an easy reference to a DOM element represented as a `NodeRef`. It also persists across renders.

## Example

Expand Down Expand Up @@ -48,3 +48,7 @@ to perform actions each time an element is rendered and just before its going to
DOM.

:::

:::info
[Learn more about `NodeRef`](../html/node-refs)
:::
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ title: "Node Refs"
description: "Out-of-band DOM access"
---

The `ref` keyword can be used inside of any HTML element or component to get the DOM `Element` that
the item is attached to. This can be used to make changes to the DOM outside of the `view` lifecycle
method.
The `ref` keyword can be used inside of any HTML element to get the DOM `Element` that the item is
attached to. This can be used to make changes to the DOM outside of the `view` lifecycle method.

This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.

Expand Down
2 changes: 1 addition & 1 deletion website/sidebars/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ module.exports = {
label: "Struct Components",
link: { type: "doc", id: "advanced-topics/struct-components/introduction" },
items: [
"advanced-topics/struct-components/hoc",
"advanced-topics/struct-components/introduction",
"advanced-topics/struct-components/lifecycle",
"advanced-topics/struct-components/scope",
"advanced-topics/struct-components/callbacks",
"advanced-topics/struct-components/properties",
"advanced-topics/struct-components/refs",
"advanced-topics/struct-components/hoc",
],
},
"advanced-topics/children",
Expand Down

0 comments on commit 9fd3f67

Please sign in to comment.