Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hobofan/yew-react-example

This project shows how to create a web app using a React component inside a Yew component.
https://github.com/hobofan/yew-react-example

Last synced: 22 days ago
JSON representation

This project shows how to create a web app using a React component inside a Yew component.

Awesome Lists containing this project

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)**