# yew-react-example
**Repository Path**: swif/yew-react-example
## Basic Information
- **Project Name**: yew-react-example
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2023-07-13
- **Last Updated**: 2024-06-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## About
This template shows how to create a web app using a React component inside a Yew component.
Similar to `yew`, this uses `web_sys` by default, but there is also a `stdweb` variant.
## 🚴 Usage
### 🛠️ Build with `npm run build`
```
yarn run build
```
### 🔬 Serve locally with `yarn npm start:dev`
```
npm run start:dev
```
## 🔎 Explanation
### Including dependencies
In the `index.html`, we include `react` and `react-dom` as UMD packages (See [React docs](https://reactjs.org/docs/cdn-links.html)).
Additionally we include `material-ui` so that we have some components available the we can use in the example.
```html
```
### Yew component that uses a React component
Inside [src/react.rs](./src/react.rs) (`web_sys` variant) [src/react_stdweb.rs](./src/react_stdweb.rs) (`stdweb` variant) you can find the Yew component `ReactCounter` that internally uses a React component to display a button with an incrementing counter.
#### Constructor (fn create)
In the `create` function, we [create a new element](https://docs.rs/stdweb/0.4.18/stdweb/web/struct.Document.html#method.create_element), which we will later use to render the React component into:
##### web_sys variant
```rust
fn create(props: Self::Properties, mut link: ComponentLink) -> Self {
ReactCounter {
// ...
node: Node::from(
web_sys::window()
.unwrap()
.document()
.unwrap()
.create_element("div")
.unwrap(),
),
// ...
}
}
```
##### stdweb variant
```rust
fn create(props: Self::Properties, mut link: ComponentLink) -> Self {
ReactCounter {
// ...
node: stdweb::web::document()
.create_element("div")
.unwrap()
.try_into()
.unwrap(),
// ...
}
}
```
We also create a [Callback](https://docs.rs/yew/0.16.0/yew/callback/struct.Callback.html) wrapper, which we need to create a Message for our Component from a JS callback:
```rust
fn create(props: Self::Properties, mut link: ComponentLink) -> Self {
ReactCounter {
// ...
react_counter_cb: Self::link_react_counter_cb(&mut link),
// ...
}
}
```
#### Rendering the component (fn view) (stdweb variant)
First we create a closure, that triggers our Callback wrapper, which we can use in the `js!` macro:
```rust
impl Renderable for ReactCounter {
fn view(&self) -> Html {
let orig_callback = self.react_counter_cb.clone();
let callback = move || orig_callback.emit(());
// ...
}
}
```
We prepare a label with the counter that we will then pass to the React component as a prop:
```rust
impl Renderable for ReactCounter {
fn view(&self) -> Html {
// ...
let label = format!(
"Native count: {} - React count: {}",
self.props.native_counter, self.react_counter
);
// ...
}
}
```
Now we come to the rendering of the React component.
Inside the `js!` macro we first create a React element instance of the `MaterialUI.Chip` component (`MaterialUI.Button` has more complicated props requirements).
As a second argument we pass in the props as an object that contains both our label and the callback which serves as a `onClick` handler.
We then use `ReactDOM.render` to render the React element into the Node we created earlier.
```rust
impl Renderable for ReactCounter {
fn view(&self) -> Html {
// ...
js! {
let element = React.createElement(MaterialUI.Chip,
{
label: @{label},
onClick: () => @{callback}(),
}
);
ReactDOM.render(element, @{self.node.clone()});
}
// ...
}
}
```
Lastly we return the node we are rendering into as a virtual DOM reference from the `view` function, so the Yew renderer knows where to attach it to in the Yew component tree.
```rust
impl Renderable for ReactCounter {
fn view(&self) -> Html {
// ...
yew::virtual_dom::VNode::VRef(self.node.clone())
}
}
```
Here is a complete view of the `view` function:
```rust
impl Renderable for ReactCounter {
fn view(&self) -> Html {
// Wrap callback in a closure that we can use in the js! macro
let orig_callback = self.react_counter_cb.clone();
let callback = move || orig_callback.emit(());
let label = format!(
"Native count: {} - React count: {}",
self.props.native_counter, self.react_counter
);
js! {
let element = React.createElement(MaterialUI.Chip,
{
label: @{label},
onClick: () => @{callback}(),
}
);
ReactDOM.render(element, @{self.node.clone()});
}
yew::virtual_dom::VNode::VRef(self.node.clone())
}
}
```
## 🙏 Acknowledgements
**Based on [yew-wasm-pack-template](https://github.com/yewstack/yew-wasm-pack-template)**