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