# 12: Reacting to React ###### tags: `Tag(sp22)` ## Logistics * Sprint 3 is not meant to be a "2 week assignment in 1.33 weeks"; I adjusted the targets with the shortened timeframe in mind. * I left in "Week 1" and "Week 2" as approximations of what we usually have. * Watch the gear-up. Seriously. Even if you watch it at 2x speed. * Make sure you're not taking on unnecessary work in this sprint. If you're unsure, check the gear up and megathread, then _ask_. Some examples: * You don't need to integrate the front-end UI elements for insertion, etc. yet. They're just meant to be mocks that show you're thinking about the UI and your stakeholders. * Our stencil had "toggling" between tables in it; you don't need to do this. Alternatives include loading a single table with a new REPL command. * You're not required to use React in Sprint 3. * However... * The table viz and DB integration person should be working closely. These components should be co-designed, and we arranged the early milestone expecting that the DB integration person would be collaborating. ## Sprint 3 Technical Comments ### Notes on Speech Synthesis ```javascript const synth = window.speechSynthesis let utterance = new SpeechSynthesisUtterance("Hello fellow students!"); utterance.onend = () => speechSynthesis.speak(new SpeechSynthesisUtterance("I would like a nap.")) synth.speak(utterance); synth.speak(new SpeechSynthesisUtterance("I don't like daylight saving time.")); ``` Which order do you think these will be spoken in? ### Specification Antipatterns Again, you aren't required to do part 1. Do your due diligence, but don't inflate the workload unnecessarily. Meet for an hour and quickly walk through the set of questions. Some will require more work. Figure out who is following up on those. Fill in everything else. Move on. Interpret the instruction to "finish your spec before writing code" as not including experimentation or prototyping. You _do not_ need to wait for a specs meeting before you experiment with (e.g.) text-to-speech. ## Reactivity ### Most Languages Consider what happens when I assign a value in JavaScript (or Java, or C, or most programming languages): ```javascript x = 10; y = x + 1; ``` At this point, `x` contains the value `10` and `y` contains the value `11`. But suppose I then update the value of `x` to `50`: ```javascript x = 50; ``` _What happens to the value of `y`_? In most languages, it remains set to `11`. That `x + 1` is only computed once, at the time the line executes. And, at that time, the value of `x` was 10. So what if it's now `50`? It was `10` when the assignment to `y` happened. ### Another philosophy Let's set up something similar in Google Sheets: ![](https://i.imgur.com/Uc6DO5N.png) The cell `A1` is set to 10, and the cell `B1` has been assigned `A1+1`. Like before `B1` has the value `11`. But in this setting, if I go and change `A1` to `50`, the value of `B1` automatically updates to `51`: ![](https://i.imgur.com/aaMMjcf.png) Languages where assignment works like it does in Google Sheets are called _reactive_, because values change _in reaction to_ changes in their dependencies. At this point, you may have two questions: _Q: Is reactivity fundamental to the language, or can you build reactivity atop normal assignment?_ A: You can, with effort, implement reactivity in languages where it's not the default behavior. _Q: Where is reactivity natural, outside of a spreadsheet program?_ A: Reactivity makes sense in more domains than you might think. ### Using Reactivity Here are a few examples. * Signal propagation in electrical engineering * Updating a database view in real time * Monitoring a system (either in production or for debugging) * *Physical* interfaces like a steering wheel or thermostat Here are two more examples in a bit more detail: #### Updating a UI when data changes In a networked chess application, the state of the board on screen should automatically reflect the programmatic board state as it changes based on moves made. #### Updating a cache when UI changes In Google maps, if I zoom out and then drag my view from Rhode Island to New York, it would be wise for them to automatically update some locally-cached information in anticipation of needing to get more data about New York. ### How do we get reactivity? All of the above are something we could, in principle, build ourselves in vanilla JavaScript (or some other language). Unfortunately, as the application grows in complexity, this becomes tougher to do, and there are efficiency challenges. For example, if we wanted to use reactivity in our web frontend applications, it would be good to avoid updating the _entire webpage_ every time a small value changed. We'd like to update only the places in the DOM that _really need_ updating. Making updates accurate and efficient is hard work. Fortunately, many web frameworks give us (an approximation of) reactivity for free. <details> <summary>(Wait, what does "approximation" mean here?)</summary> As with everything real, there is nuance. In React (which is introduced in the next section; sorry!), state updates are _asynchronous_: you don't _make_ a state update, you _queue_ a state update to be made. This gives the library control over when updates happen, which can improve performance, but means that React is not, strictly speaking, "reactive". The [React docs](https://reactjs.org/docs/design-principles.html) say: > There is an internal joke in the team that React should have been called "Schedule" because React does not want to be fully "reactive". In other words, React is a UI-building framework that borrows concepts from reactivity. If you want to learn more about "true" reactivity, check out libraries like [RxJS](https://rxjs.dev) which focus on dataflow via an abstraction called "observables". </details> ## React React is a web framework you're starting to learn about in this week's lab. You'll be using it in Sprint 4, and in the React lab. React provides two useful features (among others): * React manages your front-end app's state centrally, and when it detects a state change it propagates that change to a _virtual copy_ of the DOM. The actual DOM then only gets updated in the specific places that actually need to be changed. This can greatly improve efficiency of complex apps. * React provides a nice declarative abstraction within JavaScript. A React component is a JavaScript function that returns a JSX expression; JSX looks a lot like HTML with holes in it where we can plug in JavaScript expressions. More on this soon. <details> <summary>Woah, wait, what about these class components I keep seeing online?</summary> We don't use them. They are somewhat outdated, from the early days of React. You'll still see them referenced online, though, and for some reason the official tutorial still used them as of Fall 2021. The React team strongly suggests that new development use functional components instead, though. We follow their advice, and so should you. </details> ### In which Tim gets Annoyed(tm) at a paywall You might recall from the JavaScript UI demo a story of me becoming Annoyed (tm) with the New York Times' paywalling of a useful puzzle. I started building a new React demo from scratch. (The vanilla JS version was actually built later.) Yeah, we're going to build a (very rough) version of the NYT puzzle ourselves. All the code I'm going to show you was created from a [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html) template, and is deployed [at this Github Pages page](https://tnelson.github.io/reactNYT/) via the very nice [gh-pages](https://www.npmjs.com/package/gh-pages) package. It was surprisingly easy to deploy the front end, and since there is no back end to the application, deployment was a snap! ### What are our components? After you ignore all of the extraneous content, the NYT puzzle is pretty simple: * 3 input boxes invite you to enter a trio of numbers. * Once you've entered numbers, you click a button to check whether those numbers are in the hidden set of sequences. * The 3 input boxes become read-only and get colored red or green depending on success or failure. * A new trio of inputs appears. So we probably need: * input boxes and a submission button; and * a notion of "attempt": one current attempt, and 0 or more past attempts. That's enough to get a very rough approximation of the puzzle, which is good enough for me! (At least, good enough for today's class.) I like to draw out a prototype UI, and circle different regions that represent important grouping in the application. E.g.: ![](https://i.imgur.com/3cIg553.png) ### A starting template So we'll define 4 function components in `App.js`, along with a function to evaluate the hidden condition for sequences. I'm going to be working largely in JavaScript for _this lecture_, although TypeScript works fine with React (and you'll be using it). The template is: ```javascript= import './App.css'; import React, { useState } from 'react'; function pattern(guess) { if(guess.length !== 3) return false; if(guess[0] >= guess[1]) return false; if(guess[1] >= guess[2]) return false; return true; } function ControlledInput(props) { return ( <div></div> ); } function OldRound(props) { return ( <div></div> ); } function NewRound(props) { return ( <div></div> ); } function App() { return ( <div></div> ); } export default App; ``` Note that I've made all the components return `<div></div>`; they do nothing interesting. But even that is kind of interesting in itself. That's not JavaScript, it's JSX: React automatically renders whatever these functions return into HTML. Here, we've made it exceptionally easy for React to do that. But not for long. All these components except `App` take a single argument, `props`, that represents information passed down from parent components. By the way, I tend to use VSCode for editing front-end code. From the console, I can spin up a webserver for local access with `npm start`, and then go to `http://localhost:3000/reactNYT` to view the app. (By default, it would be served at `localhost:3000`, but I've configured it to add `reactNYT` so that I could deploy it where I did, rather than the root of my Github pages page.) Right now, nothing but a blank page shows up. ### Application state The `App` component is the entry point into a `create-react-app` program. Let's start by adding a `NewRound` component: ```javascript function App() { return ( <div> <NewRound/> </div> ); } ``` This changes nothing, but it raises the question: how do we get the `NewRound` component to do what we want? Our application has some state. What does the state look like? We'll need at least: * the state of each text input; * some record of past guesses; and (if we want to get fancy) * maybe some text state for showing error messages and so on. Let's focus on the record of past guesses. What's the right component for that record to be kept in? If we keep the record in one big array, it's the `App` component. We don't just want to add a global variable for this, though; we want to enable React to register our updates so it can efficiently flow those updates into the UI. For this, we'll use a hook (see this week's lab for more information), and we'll pass both the value and the setter function to the `NewRound` component. We'll also tell the `NewRound` component about how to update the notification text ```javascript function App() { const [guesses, setGuesses] = useState([]); const [notification, setNotification] = useState(''); return ( <div> <NewRound setGuesses={setGuesses} setNotification={setNotification} /> {notification} </div> ); } ``` The squiggly braces contain JavaScript; the result of evaluating that JavaScript gets substituted into the JSX. As a result, the `NewRound` component will have access to the setter for `guesses`, and thus have the ability to _update_ the record. Having referred to a _NewRound_ component, we probably ought to do something in the corresponding function (which is, at the moment, empty except for a `<div>`). We've got to do a few things: * We need a place for the state of those 3 text inputs to go. We'll use another `useState` hook for this. * We need a place for the inputs to go, and the guess button. * We need to _take in_ some props---at minimum, a way to change the notification message. ```javascript function NewRound(props) { const [value0, setValue0] = useState(''); const [value1, setValue1] = useState(''); const [value2, setValue2] = useState(''); return ( <div className="new-round"> <div className="guess-round-current"> <ControlledInput value={value0} setValue={setValue0} /> <ControlledInput value={value1} setValue={setValue1} /> <ControlledInput value={value2} setValue={setValue2} /> </div> <div> <button onClick={() => { if(!isNaN(parseInt(value0)) && !isNaN(parseInt(value1)) && !isNaN(parseInt(value2))) { props.addGuess([value0,value1,value2]) setValue0('') setValue1('') setValue2('') props.setNotification('') } else { props.setNotification('Please provide a full 3-number sequence.') } }}> Guess! </button> </div> </div> ); } ``` #### Aside: why have a setter at all? (Discuss.) ## The end product I've prepared this lecture to follow suggestions from the class. There's an end product I want to get to, but I haven't written a linear ordering of steps to get there. ### Notable things #### State updates are asynchronous! Try adding a `console` write immediately after a state update (here's a snippet modified from the full code below): ```javascript=function ControlledInput(props) { return ( <input value={props.value} onChange={(ev) => {props.setValue(ev.target.value); console.log(props.value);}}></input> ); } ``` ### Code Here's the end code: ```javascript import './App.css'; import React, { useState } from 'react'; function pattern(guess) { if(guess.length !== 3) return false; if(guess[0] >= guess[1]) return false; if(guess[1] >= guess[2]) return false; return true; } function ControlledInput(props) { return ( <input value={props.value} onChange={(ev) => props.setValue(ev.target.value)}></input> ); } function OldRound(props) { return ( <div className={"guess-round-"+pattern(props.guess)}> <input value={props.guess[0]} readOnly/> <input value={props.guess[1]} readOnly/> <input value={props.guess[2]} readOnly/> </div> ); } function NewRound(props) { const [value0, setValue0] = useState(''); const [value1, setValue1] = useState(''); const [value2, setValue2] = useState(''); return ( <div className="new-round"> <div className="guess-round-current"> <ControlledInput value={value0} setValue={setValue0} /> <ControlledInput value={value1} setValue={setValue1} /> <ControlledInput value={value2} setValue={setValue2} /> </div> <div> <button onClick={() => { if(!isNaN(parseInt(value0)) && !isNaN(parseInt(value1)) && !isNaN(parseInt(value2))) { props.addGuess([value0,value1,value2]) setValue0('') setValue1('') setValue2('') props.setNotification('') } else { props.setNotification('Please provide a full 3-number sequence.') } }}> Guess! </button> </div> </div> ); } function App() { const [guesses, setGuesses] = useState([]); const [notification, setNotification] = useState(''); return ( <div className="App"> { guesses.map( (guess,guessNumber) => <OldRound guess={guess} setGuesses={() => undefined} key={guessNumber} />)} <NewRound setNotification={setNotification} addGuess={(guess) => { const newGuesses = guesses.slice(); newGuesses.push(guess) setGuesses(newGuesses) }} /> {notification} </div> ); } export default App; ``` I added only a couple of new classes to the `css` file: ```css .guess-round-false { background: #a00; border: 1px solid #999; } .guess-round-true { background: #0a0; border: 1px solid #999; } ```