<info 340/>

Interactive React

Joel Ross
Autumn 2024

It's 8am after election day...

View of the Day

  • Project Draft 2 details

  • Review: Chat Components

  • React Events

  • Managing State

  • Working with Forms

Project Draft 2

What we are looking for: Refactored Draft 1 into a React App 

Converted the HTML/CSS from draft 1 into a published React app. Began to add interactive functionality.

  • ALL of content rendered: You should have Components that render DOM for every part of your page
  • Has Components w/ props and data: Organize your Components! Can hard-code sample data for now
  • Uses routes to show all page content ("next" week)
  • Has 1 feature implemented: Includes event handling and state manipulation (today). Can be the small feature!
  • Fixes issues from draft 1: You're revising the HTML/CSS, fix problems while you're at it!
  • Published to Firebase hosting: get that working this draft
    (see assignment for details; "next" week)
# switch to starter branch to get new starter code
git checkout starter

# download new starter code
git pull

# switch back to main branch for coding
git checkout main

# merge in new starter code (use default msg)
git merge starter --no-edit

# code and enjoy!

Get the starter code from the starter branch, but do all of your work on main.

Updating Lecture Code

Vite Dev Server

Vite provides a development server which will:

  • Automatically transpile React code into pure JavaScript
  • Manage module dependencies, including external libraries
  • Show build and syntax errors in the console
  • Automatically reload the page (replaces live-server)!
# Make sure you are in the project directory
cd path/to/project

# Install dependencies for existing project (installs vite)
npm install

# Run the development server script
npm run dev -- --host

Our chat app so far...

Let's make a chat app!

React Style Guidelines

React Events

We add user interaction in React the same way as with the DOM: by listening for events and executing callback functions when they occur. 

function MyButton() {
  //A function that will be called when clicked 
  //The name is conventional, but arbitrary.
  //The callback will be passed the DOM event as usual
  const handleClick = function(event) {
    console.log("clicky clicky");
  }

  //make a button with an `onClick` attribute!
  //this "registers" the listener and sets the callback
  return <button onClick={handleClick}>Click me!</button>;
}

special React prop

can only put listeners on HTML
elements, not Components!

function MyButton(props) {
  //A function that will be called when clicked 
  //The name is conventional, but arbitrary.
  //The callback will be passed the DOM event as usual
  const handleClick = (event) => {
    console.log("clicky clicky");
  }

  //make a button with an `onClick` attribute!
  //this "registers" the listener and sets the callback
  return <button onClick={handleClick}>Click me!</button>;
}

In addition to the props, React components can also track their internal state. This keeps track of information about the Component that may change due to user interaction.

React State

State is reserved only for interactivity, that is, data that changes over time

Some examples of state data:

  1. The sorted order of child components
  2. Timers or dynamic content
  3. Which model data are shown!

DOM Interactivity

//The current "state"
const state = {
  data: [ {}, {}, {} ],
  ...
}

  
//define presentation - lots of these kinds of functions
function renderData() {
  //render all the data
  //...
}

//define user interaction
button.addEventListener('click', function() {
  //MODIFY THE STATE
  state.data[i] = ...; 

  //CLEAR OLD VIEW AND RE-RENDER CONTENT
  document.querySelector('#main').innerHTML = '';
  renderData(); //RE-RENDER CONTENT    
})

changeable data lives out here

1. modify the state data

2. re-render the view

2. re-render the view

On button click, do 2 things:

You add state to a component by using a state hook. The hook defines a "state variable" which will retain its value across Component function calls, as well as a function to update that variable.

Using State Hooks

//import the state hook function `useState()` to define state
import React, { useState } from 'react';

function CountingButton(props) {
  const [count, setCount] = useState(0);

  
  
 
  const handleClick = (event) => {
    setCount(count+1); //update the state to be a new value
                       //and RE-RENDER the Component!
  }

  return (
      <button onClick={handleClick}>Clicked {count} times</button>
  );
}

state variable

update function

initial value for variable

Naming Conventions Matter!

In order to write correct React (that can be understood
and debugged by you and others), you need to follow the naming conventions:

  • The argument to a Component function is called props (with an s)
     
  • A "state-setter" function for state variable foo is called setFoo (replacing "foo" with the state variable name)

Changing State

React state is changed asynchronously.
Calling a "state-setter" function automatically re-renders the Component (by calling the function again), in order to get the updated state value.

function CountingButton(props) {
  const [count, setCount] = useState(3) //initial value of 3

  console.log(count); //will have "current" value of state
                      //3 first render, 4 after clicking
  
  const handleClick = (event) => {
    setCount(4); //change the state (but not the local variable)
    			 //AND re-render!
    console.log(count); //will output "3"; 
                        //local variable has not changed yet!
  }  
 
  return (
    <button onClick={handleClick}>Clicked {count} times</button>
  );    
}

Debugging State

Because state changes are asynchronous, you can only "see" them after the component has re-rendered. Use console logs at the "rendering" step to debug 

function CountingButton(props) {
  const [count, setCount] = useState(3) //initial value of 3

  console.log("DEBUG: count", count); //debug variable here, 
                                      //after re-render
  
  
  const handleClick = (event) => {
    setCount(count + 1); //increment count AND re-render!
    //do not debug variable here!
  }  
  
  return (
    <button onClick={handleClick}>Clicked {count} times</button>
  );    
}

Multiple State Variables

Components can (and often do) contain multiple state variables.

//Example from React documentation
function ExampleWithManyStates(props) {
  //Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  
  //...  
}

state variable is an array of objects!

State & Arrays/Objects

State variables will only be updated if a different value is passed to the setter function. For arrays and objects, pass a copy of the element with an updated element or property.

function TodoListWithError(props) {
  //a state value that is an array of objects
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

  const handleClick = (event) => {
    todos[0].text = "Fix bugs"; //modify the object 
                                //but don't make a new one
    setTodos(todos) //This won't work! Not "changing"
  }
  
  //...
}
function TodoList(props) {
  //a state value that is an array of objects
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

  const handleClick = (event) => {
    //create a copy of the array using the `map()` function
    const todosCopy = todos.map((todoObject, index) => {
      const objCopy = {...todoObject}; //copy object as well
      if(index == 0) { //transform objects if needed
        objCopy.text = "Fix bugs"
      }
      return objCopy; //return object to go into new array
    })
    setTodos(todosCopy) //This works!
  }
  
  //...
}

Props vs State

props are for information that doesn’t change from the Component’s perspective, including “initial” data. state is for information that will change, usually due to user interaction (see React FAQ).

  1. Is the value passed in from a parent via props? If so, it probably isn’t state.
     
  2. Does the value remain unchanged within a single page viewing? If so, it definitely isn’t state.
     
  3. Can you compute it based on any other state or props in your component? If so, it definitely isn’t state.

props are for information that doesn’t change from the Component’s perspective, including “initial” data. state is for information that will change, usually due to user interaction (see React FAQ).

React Form Inputs

To access the value in an <input>, save that value in the state (and update it onChange). This is called a controlled form.

use DOM event to refer to which input element

function MyInput(props) {
  const [inputValue, setInputValue] = useState('')
  
  const handleChange = (event) => {
    let newValue = event.target.value
    setInputValue(newValue);
  }

  return (
    <div>
      <input type="text" onChange={handleChange} value={inputValue} />
      You typed: {value}
    </div>);
  )
}

ALL FORMS MUST BE CONTROLLED

Passing Callbacks

function App(props) {
  const [data, setData] = useState([]);
  
  const addItemToData = (newItem) => {
    const newData = [...data, newItem]; //copy via spread
    setData(newData); //update state
  }

  return (
    <FruitButton callback={addItemToData} text={"Apple"} />
    <FruitButton callback={addItemToData} text={"Banana"} />
  )
}

function FruitButton(props) {
  //what to do when clicked
  const handleClick = (event) => {
    //call given callback, passing in given text
    props.callback(props.text);   
  }

  return (
    <button onClick={handleClick}>{props.text}</button>
  )
}

To allow child components to "update" the parent's state, pass them a callback function as a prop.
Style Guide: do not pass a state setter function directly.

Lifting Up State

If multiple components rely on the same data (variable), you should "lift up" that state to a shared parent, who can pass the information back down as props.

ChildA

ChildB

Parent

Has Data
(state)

Needs Data

<ChildA data={data} />

Has Data (prop)

Has Data (prop)

<ChildA data={data} />

Has Data (state)

Action Items!

Action Items!

  • Complete task list for Week 7 (all items!)

  • Review Ch 16-17: React & Interactive React

  • Problem Set 07 due next week

    • Do problem-a and problem-b this week!

 

No class on Monday (holiday), no lab next week

Next time (Wednesday): more interactive React!