ECE366 - Lesson 8

React

Instructor: Professor Hong

Review & Questions

## Intro to React
## What is React? - Javascript library - Created at Facebook - Open-sourced in 2013 - Docs: [https://reactjs.org/docs/getting-started.html](https://reactjs.org/docs/getting-started.html)
## What is Javascript? - High level, interpreted language - Allows you to implement complex features on web pages - Used with HTML and CSS (for styling) - Logging: ```console.log();```
## Setup/Installation - Add React Developer Tools from Chrome Extensions and add to chrome - Inspect Element -> Components - Can also install on Firefox
## React in Index.html ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin ></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin ></script> <title>React ⚛️</title> </head> <body> <h1>Getting Started with React</h1> </body> </html> ```
## React in Index.html ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> <title>React ⚛️</title> </head> <body> <div id="root"></div> <script type="text/javascript"> ReactDOM.render( React.createElement("h1", null, "Getting Started!"), document.getElementById("root") ); </script> </body> </html> ```
## ReactDOM.render ReactDOM.render() - 2 arguments - what to create: React.createElement("h1", null, "TEXT") - where to put it: document.getElementById("root")
## Variable Names ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin ></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin ></script> <title>React ⚛️</title> </head> <body> <div id="root"></div> <script type="text/javascript"> let heading = React.createElement( "h1", { style: { color: "blue" } }, "Heyyyy Everyone!" ); ReactDOM.render( heading, document.getElementById("root") ); </script> </body> </html> ```
## JSX, Babel ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <title>React ⚛️</title> </head> <body> <div id="root"></div> <script type="text/babel"> ReactDOM.render( <ul> <li>🤖</li> <li>🤠</li> <li>🌝</li> </ul>, document.getElementById("root") ); </script> </body> </html> ``` - Javascript XML; extension of JS for React JS - Can access variables with {variable}
## More JSX syntax ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin ></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin ></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <title>React ⚛️</title> </head> <body> <div id="root"></div> <script type="text/babel"> let robot = "🤖"; let cowboy = "🤠"; let moon = "🌝"; let name = "React"; ReactDOM.render( <ul> <li>{robot}</li> <li>{cowboy}</li> <li>{moon}</li> <li>{name.toUpperCase()}</li> <li>{name.length}</li> </ul>, document.getElementById("root") ); </script> </body> </html> ```
## React Components - A function that returns JSX and can be rendered in DOM - Should use capital letter for component/function name - Multiple React components should be wrapped into 1 component - Can be used to display dynamic data
## Component Properties - Add props argument to component functions - A container where you can put any properties in the component
## Example with React Components and Image ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin ></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin ></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <title>RPS</title> </head> <body> <div id="root"></div> <script type="text/babel"> function Header(props) { return ( <header> <h1>{props.name}'s RPS</h1> </header> ); } function Main(props) { return ( <section> <img height={200} src="./rps.png" alt="Rock Paper Scissors" /> <ul> {props.users.map((user) => ( <li key={user.id}> {user.title} </li> ))} </ul> </section> ); } function Footer(props) { return ( <footer> <p>Copyright {props.year}</p> </footer> ); } const users = [ "Alpha", "Beta", "Gamma", ]; const userObjects = users.map( (user, i) => ({ id: i, title: user }) ); function App() { return ( <React.Fragment> <Header name="Cooperps" /> <Main adjective="amazing" users={userObjects} /> <Footer year={new Date().getFullYear()} /> </React.Fragment> ); } ReactDOM.render( <App />, document.getElementById("root") ); </script> </body> </html> ```
## React App
## Create React App - Install nodejs - [https://nodejs.org/](https://nodejs.org/) ``` sudo apt update sudo apt install nodejs ``` - ```node -v``` - to check node version - ```npm -v``` - check npm version - ```npx create-react-app rps``` - ```npm start```
## Important Parts - package.json - dependencies - react, react-dom, react-scripts - src/index.js - entry point of the application, main JS file - renders App - React.StrictMode - additional checks - public/index.html - similar to index.html w/ all your JSX injected ``` npm install npm start ```
## Javascript Destructuring Allows you to get specific elements from an argument/props ``` function App({ myVariable }) { return ( <div className="App"> <h1>{myVariable}</h1> </div> ); } ``` - Also Array destructuring allows you to get certain elements from an array
## Changing States with useState App.js ``` import "./App.css"; import { useState } from "react"; function App() { const [emotion, setEmotion] = useState("happy"); return ( <div className="App"> <h1>Current emotion is {emotion}</h1> <button onClick={() => setEmotion("sad")}> Sad </button> <button onClick={() => setEmotion("excited")} > Excited </button> </div> ); } export default App; ```
## Side Effects with useEffect ``` import "./App.css"; import { useState, useEffect } from "react"; function App() { const [emotion, setEmotion] = useState("happy"); const [secondary, setSecondary] = useState("tired"); useEffect(() => { console.log(`It's ${emotion} around here!`); }, [emotion]); useEffect(() => { console.log(`It's ${secondary} around here!`); }, [secondary]); return ( <div className="App"> <h1>Current emotion is {emotion}</h1> <button onClick={() => setEmotion("sad")}> Sad </button> <button onClick={() => setEmotion("excited")} > Excited </button> <h2> Current secondary emotion is {secondary}. </h2> <button onClick={() => setSecondary("grateful")} > Grateful </button> </div> ); } export default App; ``` - dependencyArray is the 2nd argument for useEffect - No argument makes it called everytime - Empty array makes it called once
## Fetching Data from Service fetch is built into the browser to make an HTTP request to get data ``` import "./App.css"; import { useState, useEffect } from "react"; function App() { const [data, setData] = useState(null); useEffect(() => { fetch( `http://localhost:8080/api/users` ) .then((response) => response.json()) .then(setData); }, []); if (data) return ( <pre>{JSON.stringify(data, null, 2)}</pre> ); return <h1>Data</h1>; } export default App; ```
## Update CORS in Spring Boot server WebserviceController.java ``` @CrossOrigin @GetMapping("/users") ```
## More React
## React Forms useRef - a hook that is going to reach out to some UI element and get its value, doesn't re-render ``` import "./App.css"; import { useRef } from "react"; function App() { const txtTitle = useRef(); const hexColor = useRef(); const submit = (e) => { e.preventDefault(); const title = txtTitle.current.value; const color = hexColor.current.value; alert(`${title}, ${color}`); txtTitle.current.value = ""; hexColor.current.value = ""; }; return ( <form onSubmit={submit}> <input ref={txtTitle} type="text" placeholder="color title..." /> <input ref={hexColor} type="color" /> <button>ADD</button> </form> ); } export default App; ```
## React Forms with useState re-renders at the end ``` import "./App.css"; import { useState } from "react"; function App() { const [title, setTitle] = useState(""); const [color, setColor] = useState("#000000"); const submit = (e) => { e.preventDefault(); alert(`${title}, ${color}`); setTitle(""); setColor("#000000"); }; return ( <form onSubmit={submit}> <input value={title} onChange={(event) => setTitle(event.target.value) } type="text" placeholder="color title..." /> <input value={color} type="color" onChange={(event) => setColor(event.target.value) } /> <button>ADD</button> </form> ); } export default App; ```
## Form Libraries - formik - react hook forms
## React Router To install: ``` npm install react-router-dom ```
## Configuring the Router App.js ``` import "./App.css"; function Home() { return ( <div> <h1>My Website</h1> </div> ); } export function About() { return ( <div> <h1>About Us</h1> </div> ); } export function Contact() { return ( <div> <h1>Contact Us</h1> </div> ); } export function App() { return <Home />; } ```
## Configuring the Router index.js ``` import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import { App, About, Contact } from "./App"; import { BrowserRouter, Routes, Route } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <Routes> <Route path="/" element={<App />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> </Routes> </BrowserRouter>, ); ```
## Adding Links to the Router App.js ``` import "./App.css"; import { Link } from "react-router-dom"; function Home() { return ( <div> <nav> <Link to="/about">About</Link> <Link to="/contact">Contact</Link> </nav> <h1>My Website</h1> </div> ); } export function About() { return ( <div> <nav> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/contact">Contact</Link> </nav> <h1>About Us</h1> </div> ); } export function Contact() { return ( <div> <nav> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/contact">Contact</Link> </nav> <h1>Contact Us</h1> </div> ); } export function App() { return <Home />; } ```
## Websocket + STOMP
## STOMP - Simple/Streaming Text Oriented Message Protocol - Allows you to create an interactive web application - The web is the STOMP client - The service is the message broker - Example from [https://spring.io/guides/gs/messaging-stomp-websocket/](https://spring.io/guides/gs/messaging-stomp-websocket/)
## Pre-initialized Spring Initializr [Pre-initialized Initializr](https://start.spring.io/#!type=maven-project&groupId=com.example&artifactId=messaging-stomp-websocket&name=messaging-stomp-websocket&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.messaging-stomp-websocket&dependencies=websocket) Additional pom.xml dependencies ``` <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.1-1</version> </dependency> ```
## HelloMessage Class HelloMessage.java ``` package com.example.messagingstompwebsocket; public class HelloMessage { private String name; public HelloMessage() { } public HelloMessage(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ```
## Greeting Class Greeting.java ``` package com.example.messagingstompwebsocket; public class Greeting { private String content; public Greeting() { } public Greeting(String content) { this.content = content; } public String getContent() { return content; } } ```
## GreetingController Class GreetingController.java ``` package com.example.messagingstompwebsocket; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; import org.springframework.web.util.HtmlUtils; @Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // simulated delay return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); } } ```
## WebSocketConfig Class WebSocketConfig.java ``` package com.example.messagingstompwebsocket; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").withSockJS(); } } ```
## Index.html src/main/resources/static/index.html ``` <!DOCTYPE html> <html> <head> <title>Hello WebSocket</title> <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="/main.css" rel="stylesheet"> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/app.js"></script> </head> <body> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div id="main-content" class="container"> <div class="row"> <div class="col-md-6"> <form class="form-inline"> <div class="form-group"> <label for="connect">WebSocket connection:</label> <button id="connect" class="btn btn-default" type="submit">Connect</button> <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect </button> </div> </form> </div> <div class="col-md-6"> <form class="form-inline"> <div class="form-group"> <label for="name">What is your name?</label> <input type="text" id="name" class="form-control" placeholder="Your name here..."> </div> <button id="send" class="btn btn-default" type="submit">Send</button> </form> </div> </div> <div class="row"> <div class="col-md-12"> <table id="conversation" class="table table-striped"> <thead> <tr> <th>Greetings</th> </tr> </thead> <tbody id="greetings"> </tbody> </table> </div> </div> </div> </body> </html> ```
## app.js src/main/resources/static/app.html ``` var stompClient = null; function setConnected(connected) { $("#connect").prop("disabled", connected); $("#disconnect").prop("disabled", !connected); if (connected) { $("#conversation").show(); } else { $("#conversation").hide(); } $("#greetings").html(""); } function connect() { var socket = new SockJS('/gs-guide-websocket'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/greetings', function (greeting) { showGreeting(JSON.parse(greeting.body).content); }); }); } function disconnect() { if (stompClient !== null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); } function showGreeting(message) { $("#greetings").append("" + message + ""); } $(function () { $("form").on('submit', function (e) { e.preventDefault(); }); $( "#connect" ).click(function() { connect(); }); $( "#disconnect" ).click(function() { disconnect(); }); $( "#send" ).click(function() { sendName(); }); }); ```
## RPS Game Logic - Add server side logic - Add UI to communicate with server