Requirements:
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
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!
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
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`)
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.
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.
State is reserved only for interactivity, that is, data that changes over time
Some examples of state data:
Initialize a Components state in its constructor, and then access it when view is rendered.
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!
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 AsynchronousThe "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 AsynchronousPass 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>
);
}
}
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)
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>
}
}
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
state
state
in the "lowest" common ancestor for Components that need it
state
information to child Components as
props
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.
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>
);
}
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"
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
}
}
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
}
}
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