<info 340/>
Interactive React
Joel Ross
Winter 2020
Plan for Today
-
Stage 3 Requirements
-
Interactive React Demo
- Review: Components & Props
- Events
- State
- Lifecycle [if time]
Requirements:
- Built in React (uses Components, prop, state)
- Same level of functionality as Stage 2 (1 complete feature)
- > we're aiming to give Stage 2 feedback over weekend
- Use an external component library (examples next week)
- e.g., "bootstrap for react", "font-awesome for react"
- Well-designed user experience (Stage 1-esque work)
- Accessible, responsive, etc
- Be sure to include feedback on user actions!
- Deployed (to Firebase Hosting; next week)
Project: Stage 3
Objective: redo your Stage 2 in React!
Questions on anything?
React Review
class App extends Component {
render() {
return (
<main>
<MessageBanner message="I'm such a prop!" />
</main>
);
}
}
class MessageBanner extends Component {
render() {
let msg = this.props.message.toUpperCase();
return <p className="banner">{msg}</p>;
}
}
ReactDOM.render(<App />,
document.getElementById('root'));
define components
render method returns displayed DOM
JSX: shortcut for creating elements!
pass "props" as attributes
access passed-in props
include inline expressions
render element
React Events
We add user interaction in React the same way as the DOM and jQuery: by listening for events and executing callback functions when they occur.
class MyButton extends Component {
//method to call when clicked (name is arbitrary)
doSomething(evt) {
console.log('clicky clicky');
}
render() {
//make a button with an `onClick` attribute!
//this "registers" the listener and sets the callback
return (
<button onClick={this.doSomething}>
Click me!
</button>
);
}
}
special React prop
can only put listeners on HTML elements, not Components!
Retaining this
Remember that callbacks are not called on anything, so in order to access this you need to...
... bind() the this value to the function in the constructor
class MyButton extends Component {
constructor(props) {
super(props);
this.doSomething = this.doSomething.bind(this);
}
doSomething(evt) {
console.log(this.props.dataValue);
}
render() {
return (
<button onClick={this.doSomething}>
Click me!
</button>
);
}
}
Component constructor takes in props
Must call superclass's constructor too!
Create "bound" version
of function and reassign
Retaining this
Remember that callbacks are not called on anything, so in order to access this you need to...
... "wrap" the method to call in an arrow function
class MyButton extends Component {
//no constructor work necessary!
doSomething(evt) {
console.log(this.props.dataValue);
}
render() {
return (
<button onClick={(evt) => this.doSomething(evt)}>
Click me!
</button>
);
}
}
Creates a function (with `this`) that can call the method (with `this`)
Retaining
this
class MyButton extends Component {
//no constructor work necessary!
//assign the arrow function to a variable (and pass that)
doSomething = (evt) => {
console.log(this.props.dataValue);
}
render() {
return (
<button onClick={this.doSomething}>
Click me!
</button>
);
}
}
Remember that callbacks are not called on anything, so in order to access this you need to...
... use a public class field.
React Props
A Component's props are information received from the "outside" that describe that component.
props are a Component's configuration, its options if you may. They are received from above and immutable as far as the Component receiving them is concerned. A Component cannot change its props, but it is responsible for putting together the props of its child Components.
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:
- The sorted order of child components
- Timers or dynamic content
- Which model data are shown!
Initialize a Components state in its constructor, and then access it when view is rendered.
Using State
class CountingButton extends React.Component {
constructor(props) {
super(props)
this.state = {count: 0}
}
handleClick = () => {
console.log("You clicked me!");
}
render() {
return (
<button onClick={this.handleClick}>
Clicked {this.state.count} times
</button>
);
}
}
Assign value to `state` instance variable. Must be an object.
Access state. If it's not used in render(), it shouldn't be state!
Changing State
React state needs to be changed asynchronously (for speed). Modify
this.state
object using the component's
setState()
method. Calling this method will update the state (when ready) and
automatically re-render the Component (via render()
).
class CountingButton extends React.Component {
constructor(props) {
super(props)
this.state = {count: 0}
}
handleClick = () => {
let stateChanges = {count: 1}
this.setState(stateChanges); //change the state
}
render() {
return (
<button onClick={this.handleClick} >
Clicked {this.state.count} times
</button>
);
}
}
Use method to change state and re-render (will call `render()`)
Parameter object only needs values that have changed (will "merge")
setState()
is Asynchronous
The "updated" state value will only be available when the component is re-rendered (so from the render() function).
class CountingButton extends React.Component {
constructor(props) {
super(props)
this.state = {count: 0}
}
handleClick = () => {
let stateChanges = {count: 1}
this.setState(stateChanges); //change the state
console.log(this.state.count) // still 0! hasn't changed yet
}
render() {
return (
<button onClick={this.handleClick} >
Clicked {this.state.count} times
</button>
);
}
}
setState()
is Asynchronous
Pass a callback function to setState() if you want to modify based on current state.
class CountingButton extends React.Component {
constructor(props) {
super(props)
this.state = {count: 0}
}
handleClick = () => {
this.setState((currState, currProps) => {
let stateChanges = {count: currState.count + 1}
return stateChanges; //return the value to "set"
});
}
render() {
return (
<button onClick={this.handleClick} >
Clicked {this.state.count} times
</button>
);
}
}
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
Needs Data
prop = state;
prop = state;
Has Data (prop)
Has Data (prop)
Passing Callbacks
Often when an event occurs, you want the parent component to do something. The parent passes in a callback function as a prop for the child to execute!
Use arrow functions to bind the this.
class App extends Component {
handleClick = (thing) => {
console.log("You clicked on", thing);
//can call `this.setState()` here!
}
render() {
return <MyButton callback={this.handleClick} text={"Hello"} />
}
}
class MyButton extends Component {
handleClick = (event) => {
//call given callback, passing in given text
this.props.callback(this.props.text);
}
render() {
return <button onClick={this.handleClick}>{this.props.text}</button>
}
}
React Inputs
To access the
value
in an
<input>
, save that value in the
state
(and update it
onChange
). This is called a controlled form.
class MyInput extends React.Component {
constructor(props) {
super(props)
this.state = {value: ''}
}
handleChange = (event) => {
let newValue = event.target.value
this.setState({value:newValue});
}
render() {
return (
<div>
<input type="text" onChange={this.handleChange} />
You typed: {this.state.value}
</div>);
}
}
use DOM event to refer to the element
Making React Interactive
-
Start with a "static" (non-interactive) version, with appropriate Components
-
Identify variables that need to be
state
-
Put
state
in the "lowest" common ancestor for Components that need it -
Pass
state
information to child Components asprops
-
Pass callback functions as
props
to child Components so they can modify the parent state.
React Hooks are a recent (v16.8, Oct 2018) alternative to defining classes and life-cycle methods. We will not be using them in this class, as they're less clear (more "black magic"). Stick with classes and class methods.
React Hooks
function CountingButton() {
//Declare the state variable `count` and a setter for it
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
</div>
);
}
Loading Data
To use fetch() in React (which is compiled via webpack & Node), you will need to install and import the polyfill.
# Install polyfill (on command line)
npm install --save whatwg-fetch
//import polyfill (in JavaScript)
import 'whatwg-fetch'; //loading "globally"
Component Lifecycle
The Component class has methods that are called at each stage of the component's "Lifecycle". Override these methods to perform an action at that point.
class MyComponent extends Component {
constructor(props){
super(props)
//called at Compnent instantiation (not on screen yet)
//initialize state here
}
componentDidMount() {
//called when put on screen (visible, after render())
//do AJAX requests here!
}
componentDidUpdate(revProps, prevState, snapshot) {
//called when changing. Less common
}
componentWillUnmount() {
//called when removed from screen
//do asynchronous cleanup here
}
}
AJAX & React
Send an
AJAX request from the
componentDidMount()
method; the
.then()
callback should update the state when new data arrives!
class MyComponent extends Componet {
constructor(props){
super(props);
this.state = {data: []}; //initialize as empty!
}
componentDidMount() {
fetch(dataUri) //send AJAX request
.then((res) => res.json())
.then((data) => {
let processedData = data.filter().map() //process the data
this.setState({data: processedData}) //change the state!
})
}
render() {
//convert data into DOM
//Note that this works even before data is loaded (when empty!)
let dataItems = this.state.data.map((item) => {
return <li>{item.value}</li>; //get item from data
})
return (<ul>{dataItems}</ul>); //render the list
}
}
Action Items!
-
Finish Problem Set 08
-
And any others!
-
-
Project: Start on this!
-
Get set up; make a few components, etc.
-
Let us know if you're changing groups
-
-
Reading: Chapter 16-17
Next time: React lifecycle, libraries, etc
info340wi20-react-interactive
By Joel Ross
info340wi20-react-interactive
- 502