$ npm install -g create-react-app
$ create-react-app counter
delete everything except for index.js, registerServiceWorker.js
create components/
folder
create App.js
const App = () => <h1>Counter App</h1>
export default App
- EXPLAIN functional components and JSX
change import in index.js for App
show live reload
ADD components/Counter.js
import React from 'react'
export default class Counter extends React.Component {
}
- EXPLAIN class syntax
- EXPLAIN export import syntax
add render()
method
render () {
return (
<div>
<p>Count: 0</p>
<button>Increment</button>
</div>
)
}
- EXPLAIN top level
<div>
wrapper - EXPLAIN using class syntax for state use
ADD onClick={this.handleIncrement}
to <button>
ADD handleIncrement () {console.log('Increment')}
SHOW console
ADD constructor ()
method
constructor (props) {
super(props)
this.handleIncrement = this.handleIncrement.bind(this)
this.state = {count: 0}
}
- EXPLAIN constructor syntax and super, binding of event handlers
ADD body to
handleIncrement ()
handleIncrement () {
this.setState((prevState) => ({count: prevState.count + 1})
}
render () {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleIncrement}>Increment</button>
</div>
)
}
SHOW increment working
What if we wanted to have more than one counter?
Need to keep "counters" state somewhere, probably in App
If we need to keep track of state, we should use class syntax
-> Convert App to class syntax
Add counters to state in constructor
constructor (props) {
super(props)
this.state = {
counters = [{id: 0}]
}
}
ADD <button>
to render
<button onClick={this.handleAddCounter}>Add Counter</button>
ADD handleAddCounter
method and binding to this
in constructor
handleAddCounter () {
this.setState(prevState => ({
counters: prevState.counters.concat({id: prevState.counters.length})
})
}
render based on state.counters
{this.state.counters.map(counter => <Counter key={counter.id} />)}
- EXPLAIN Array::concat vs Array::push
- EXPLAIN ids and
key
prop
So, maybe we would want to set a name for a Counter...
ADD <input>
to App#render
<label htmlFor="newCounterName">New Counter Name</label>
<br />
<input type="text"
value={this.state.newCounterName}
onChange={this.handleNewCounterNameChange} />
- EXPLAIN consequences of using react state for inputs
ADD this.handleNewCounterNameChange = this.handleNewCounterNameChange.bind(this)
to constructor
ADD newCounterName: ""
to initialState
ADD name: "Counter"
to initial counter in array
ADD handleNewCounterNameChange
handleNewCounterNameChange (event) {
this.setState({
newCounterName: event.target.value
})
ADD name
prop to Counter in App
ADD name
display to Counter component
SHOW name is rendered on add
But, name persists in text box
ADD newCounterName: ""
to handleAddCounter
setState
ADD onKeyPress={this.handleNewCounterNameKeyPress}
to <input>
ADD #handleNewCounterNameKeyPress
handleNewCounterNameKeyPress (event) {
if(event.key === "Enter") {
this.handleAddCounter()
}
}
We don't want to create a counter if the form is blank
ADD error: false
to initial state
ADD style={{border: this.state.error ? "5px solid red" : "1px solid"}}
to input
ADD error handling to handleAddCounter
handleAddCounter () {
if(this.state.newCounterName === "") {
this.setState({error: true})
return
}
...
}
ADD error: false
to handleNewCounterNameChange
CHANGE <h1>
in app to "Visits"
add setInterval to Counter constructor
constructor (props) {
...
if(this.props.name === "Your Mom") {
this.incrementInterval = setInterval(this.handleIncrement, 400)
}