diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 99f9c4e..e1785d2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -21,9 +21,11 @@ * [Literals & Expressions](concepts/html/literals-and-expressions.md) * [Components](concepts/html/components.md) * [Components](concepts/components/README.md) + * [Internal state](concepts/components/internalstate.md) * [Properties](concepts/components/properties.md) * [Callbacks](concepts/components/callbacks.md) * [Refs](concepts/components/refs.md) + * [Life cyle](concepts/components/lifecycle.md) * [Agents](concepts/agents.md) * [Services](concepts/services/README.md) * [Format](concepts/services/format.md) diff --git a/src/concepts/components/README.md b/src/concepts/components/README.md index aa8adb3..f1bcebd 100644 --- a/src/concepts/components/README.md +++ b/src/concepts/components/README.md @@ -1,143 +1,71 @@ --- -description: Components and their lifecycle hooks +description: Introduction of Yew component --- # Components -## What are Components? +## What are Components Components are the building blocks of Yew. They manage their own state and can render themselves to the DOM. Components are created by implementing the `Component` trait which describes the lifecycle of a component. -## Lifecycle - -{% hint style="info" %} -`Contribute to our docs:` [Add a diagram of the component lifecycle](https://github.com/yewstack/docs/issues/22) -{% endhint %} - -## Lifecycle Methods - -### Create - -When a component is created, it receives properties from its parent component as well as a `ComponentLink`. 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. - -It is common to store the props and the link in your component struct, like so: +## Example of component ```rust -pub struct MyComponent { - props: Props, - link: ComponentLink, -} +use yew::prelude::*; -impl Component for MyComponent { - type Properties = Props; - // ... +pub struct ExampleComponent { + // state of the component + name: String, + show_message: bool, - fn create(props: Self::Properties, link: ComponentLink) -> Self { - MyComponent { props, link } - } + // properties and events struct + props: Props, - // ... + // link field supports the mechanism through which components are able to register callbacks and update themselves + link: ComponentLink, } -``` - -### View - -Components declare their layout in the `view()` method. Yew provides the `html!` macro for declaring HTML and SVG nodes and their listeners as well as child components. The macro acts a lot like React's JSX, but uses Rust expressions instead of JavaScript. -```rust -impl Component for MyComponent { - // ... - - fn view(&self) -> Html { - let onclick = self.link.callback(|_| Msg::Click); - html! { - - } - } +// enum of "Messages" that will be used to mutate the component state +pub enum Msg { + Click, } -``` - -For usage details, check out the `html!` guide: - -{% page-ref page="../html/" %} - -### Rendered -The `rendered()` component lifecycle method is called after `view()` is processed and Yew has rendered your component, but before the browser refreshes the page. A component may wish to implement this method to perform actions that can only be done after the component has rendered elements. You can check whether this is the first time the component was rendered via the `first_render` parameter. +// definition of properties and events of the component +#[derive(Properties, Clone, PartialEq)] +pub struct Props{ + #[prop_or("Superman".to_string())] + pub name: String, -```rust -use stdweb::web::html_element::InputElement; -use stdweb::web::IHtmlElement; -use yew::prelude::*; - -pub struct MyComponent { - node_ref: NodeRef, + #[prop_or_default] + pub onmyclickevent:Callback, } -impl Component for MyComponent { - // ... - - fn view(&self) -> Html { - html! { - - } - } +impl Component for ExampleComponent { + type Message = Msg; + type Properties = Props; - fn rendered(&mut self, first_render: bool) { - if first_render { - if let Some(input) = self.node_ref.try_into::() { - input.focus(); - } + // Initialization of the state + fn create(props: Self::Properties, link: ComponentLink) -> Self { + Self { + link, + props: props.clone(), + name: props.name.into(), + show_message: false, } } -} -``` - -{% hint style="info" %} -Note that this lifecycle method does not require an implementation and will do nothing by default -{% endhint %} - -### Update - -Components are dynamic and can register to receive asynchronous messages. The `update()` lifecycle method is called for each message. 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 triggered by HTML elements listeners or be sent by child components, Agents, Services, or Futures. - -Here's an example of what `update()` could look like: - -```rust -pub enum Msg { - SetInputEnabled(bool) -} - -impl Component for MyComponent { - type Message = Msg; - - // ... + // This method is executed each time the link.callbacks is called + // you can mutate the state based on the message received fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetInputEnabled(enabled) => { - if self.input_enabled != enabled { - self.input_enabled = enabled; - true // Re-render - } else { - false - } - } - } - } -} -``` - -### Change - -Components may be re-rendered by their parents. When this happens, they could receive new properties and choose to re-render. This design facilitates parent to child component communication through changed properties. + match msg { -A typical implementation would look like: - -```rust -impl Component for MyComponent { - // ... + // mutate the component state + Msg::Click => self.show_message = true, + } + true + } + // you can use change method to decide if you would like to re-render when properties change fn change(&mut self, props: Self::Properties) -> ShouldRender { if self.props != props { self.props = props; @@ -146,33 +74,21 @@ impl Component for MyComponent { false } } -} -``` - -### Destroy -After Components are unmounted from the DOM, Yew calls the `destroy()` lifecycle method to support any necessary clean up operations. This method is optional and does nothing by default. - -## Associated Types - -The `Component` trait has two associated types: `Message` and `Properties`. - -```rust -impl Component for MyComponent { - type Message = Msg; - type Properties = Props; - - // ... -} -``` - -`Message` represents a variety of messages that can be processed by the component to trigger some side effect. For example, you may have a `Click` message which triggers an API request or toggles the appearance of a UI component. It is common practice to create an enum called `Msg` in your component's module and use that as the message type in the component. It is common to shorten "message" to "msg". - -```rust -enum Msg { - Click, + // Rendering of the component + fn view(&self) -> Html { + // different rendering depend on the component state + if !self.show_message { + html! { + // Listen to HTML events and trigger a message that will be managed in the update method + + } + } else { + html! { + // Use state value in the html +

{format!("Hello {}", self.name)}

+ } + } + } } ``` - -`Properties` represents the information passed to a component from its parent. This type must implements the `Properties` trait \(usually by deriving it\) and can specify whether certain properties are required or optional. This type is used when creating and updating a component. It is common practice to create a struct called `Props` in your component's module and use that as the component's `Properties` type. It is common to shorten "properties" to "props". Since props are handed down from parent components, the root component of your application typically has a `Properties` type of `()`. If you wish to specify properties for your root component, use the `App::mount_with_props` method. - diff --git a/src/concepts/components/internalstate.md b/src/concepts/components/internalstate.md new file mode 100644 index 0000000..0e4b674 --- /dev/null +++ b/src/concepts/components/internalstate.md @@ -0,0 +1,169 @@ +--- +description: Component can maintain it's own state and render information depending on it +--- + +# Internal State + +The component can manage it's own state using the Rust struct that implement the trait `Component`. The HTML rendering is based on this state. +When state change there is possibility to re-render the component. + +> TODO documentation on state mutation + +```rust +use yew::prelude::*; + +pub struct InternalStateComponent { + name:String, +} + +impl Component for InternalStateComponent { + type Message = (); + type Properties = (); + + fn create(_props: Self::Properties, _link: ComponentLink) -> Self { + Self { + name: "Clark".into(), + } + } + + fn update(&mut self, _msg: Self::Message) -> ShouldRender { + true + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender { + true + } + + fn view(&self) -> Html { + html! { + <> +

{format!("Hello {}",self.name)}

+ + } + } +} +``` + +## State definition + +Here we add the `name` field in the struct + +```rust +# use yew::prelude::*; +# +// ... +pub struct InternalStateComponent { + name:String, +} +// ... +# +# impl Component for InternalStateComponent { +# type Message = (); +# type Properties = (); +# +# fn create(_props: Self::Properties, _link: ComponentLink) -> Self { +# Self { +# name: "Clark".into(), +# } +# } +# +# fn update(&mut self, _msg: Self::Message) -> ShouldRender { +# true +# } +# +# fn change(&mut self, _props: Self::Properties) -> ShouldRender { +# true +# } +# +# fn view(&self) -> Html { +# html! { +# <> +#

{format!("Hello {}",self.name)}

+# +# } +# } +# } + +``` + +## State initialization + +The component lifecycle will initialize the state in the `create` method. + +```rust +# use yew::prelude::*; +# +# pub struct InternalStateComponent { +# name:String, +# } +# +# impl Component for InternalStateComponent { +# type Message = (); +# type Properties = (); +// ... + fn create(_props: Self::Properties, _link: ComponentLink) -> Self { + Self { + name: "Clark".into(), + } + } +// ... +# fn update(&mut self, _msg: Self::Message) -> ShouldRender { +# true +# } +# +# fn change(&mut self, _props: Self::Properties) -> ShouldRender { +# true +# } +# +# fn view(&self) -> Html { +# html! { +# <> +#

{format!("Hello {}",self.name)}

+# +# } +# } +# } +``` + +## Rendering based on the state value + +Using the `html!` macro we can render html based on the component state in the `view` method + +> please refer to the `html!` macro documentation page for more detail on the HTML rendering + +```rust +# use yew::prelude::*; +# +# pub struct InternalStateComponent { +# name:String, +# } +# +# impl Component for InternalStateComponent { +# type Message = (); +# type Properties = (); +# +# fn create(_props: Self::Properties, _link: ComponentLink) -> Self { +# Self { +# name: "Clark".into(), +# } +# } +# +# fn update(&mut self, _msg: Self::Message) -> ShouldRender { +# true +# } +# +# fn change(&mut self, _props: Self::Properties) -> ShouldRender { +# true +# } +# +// ... + fn view(&self) -> Html { + html! { + <> +

{format!("Hello {}",self.name)}

+ + } + } +// ... +# } +``` diff --git a/src/concepts/components/lifecycle.md b/src/concepts/components/lifecycle.md new file mode 100644 index 0000000..7051417 --- /dev/null +++ b/src/concepts/components/lifecycle.md @@ -0,0 +1,167 @@ +--- +description: Component life cycle +--- + +# Life cycle + +{% hint style="info" %} +`Contribute to our docs:` [Add a diagram of the component lifecycle](https://github.com/yewstack/docs/issues/22) +{% endhint %} + +## Lifecycle Methods + +### Create + +When a component is created, it receives properties from its parent component as well as a `ComponentLink`. 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. + +It is common to store the props and the link in your component struct, like so: + +```rust +pub struct MyComponent { + props: Props, + link: ComponentLink, +} + +impl Component for MyComponent { + type Properties = Props; + // ... + + fn create(props: Self::Properties, link: ComponentLink) -> Self { + MyComponent { props, link } + } + + // ... +} +``` + +### View + +Components declare their layout in the `view()` method. Yew provides the `html!` macro for declaring HTML and SVG nodes and their listeners as well as child components. The macro acts a lot like React's JSX, but uses Rust expressions instead of JavaScript. + +```rust +impl Component for MyComponent { + // ... + + fn view(&self) -> Html { + let onclick = self.link.callback(|_| Msg::Click); + html! { + + } + } +} +``` + +For usage details, check out the `html!` guide: + +{% page-ref page="../html/" %} + +### Rendered + +The `rendered()` component lifecycle method is called after `view()` is processed and Yew has rendered your component, but before the browser refreshes the page. A component may wish to implement this method to perform actions that can only be done after the component has rendered elements. You can check whether this is the first time the component was rendered via the `first_render` parameter. + +```rust +use stdweb::web::html_element::InputElement; +use stdweb::web::IHtmlElement; +use yew::prelude::*; + +pub struct MyComponent { + node_ref: NodeRef, +} + +impl Component for MyComponent { + // ... + + fn view(&self) -> Html { + html! { + + } + } + + fn rendered(&mut self, first_render: bool) { + if first_render { + if let Some(input) = self.node_ref.try_into::() { + input.focus(); + } + } + } +} +``` + +{% hint style="info" %} +Note that this lifecycle method does not require an implementation and will do nothing by default +{% endhint %} + +### Update + +Components are dynamic and can register to receive asynchronous messages. The `update()` lifecycle method is called for each message. 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 triggered by HTML elements listeners or be sent by child components, Agents, Services, or Futures. + +Here's an example of what `update()` could look like: + +```rust +pub enum Msg { + SetInputEnabled(bool) +} + +impl Component for MyComponent { + type Message = Msg; + + // ... + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + Msg::SetInputEnabled(enabled) => { + if self.input_enabled != enabled { + self.input_enabled = enabled; + true // Re-render + } else { + false + } + } + } + } +} +``` + +### Change + +Components may be re-rendered by their parents. When this happens, they could receive new properties and choose to re-render. This design facilitates parent to child component communication through changed properties. You don't have to implement `change()` but you probably want to if you want to update a component via props after it has been created. + +A naive implementation would look like: + +```rust +impl Component for MyComponent { + // ... + + fn change(&mut self, props: Self::Properties) -> ShouldRender { + self.props = props; + true // This will always re-render when new props are provided. + } +} +``` + +### Destroy + +After Components are unmounted from the DOM, Yew calls the `destroy()` lifecycle method to support any necessary clean up operations. This method is optional and does nothing by default. + +## Associated Types + +The `Component` trait has two associated types: `Message` and `Properties`. + +```rust +impl Component for MyComponent { + type Message = Msg; + type Properties = Props; + + // ... +} +``` + +`Message` represents a variety of messages that can be processed by the component to trigger some side effect. For example, you may have a `Click` message which triggers an API request or toggles the appearance of a UI component. It is common practice to create an enum called `Msg` in your component's module and use that as the message type in the component. It is common to shorten "message" to "msg". + +```rust +enum Msg { + Click, +} +``` + +`Properties` represents the information passed to a component from its parent. This type must implements the `Properties` trait \(usually by deriving it\) and can specify whether certain properties are required or optional. This type is used when creating and updating a component. It is common practice to create a struct called `Props` in your component's module and use that as the component's `Properties` type. It is common to shorten "properties" to "props". Since props are handed down from parent components, the root component of your application typically has a `Properties` type of `()`. If you wish to specify properties for your root component, use the `App::mount_with_props` method. diff --git a/src/lib.rs b/src/lib.rs index 69b0734..73762d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,3 +18,13 @@ fn test_optimizations() { fn test_properties() { doctest!("concepts/components/properties.md"); } + +#[wasm_bindgen_test] +fn test_component_readme() { + doctest!("concepts/components/README.md"); +} + +#[wasm_bindgen_test] +fn test_component_internalstate() { + doctest!("concepts/components/internalstate.md"); +} \ No newline at end of file