Say that you’ve got an <input>
, and you want to be notified when its value changes. Doing so is simple — you just pass an onChange
prop.
Okay, but let’s switch things up a little, and say that instead of using a plain <input>
you want to set its styles by wrapping it with a custom Input
component. This means that there’s no built in onChange
prop.
How will you be notified when the custom input’s value changes?
The Input
component can accept its own custom onChange
prop! In fact, the prop can be called whatever you like.
function handleChange(value) {
}
React.createElement(
Input,
{ onChange: handleChange }
)
The Input
component can then call the passed callback from within the native input event handler — or could even pass the callback directly to the internal input’s onChange
prop.
It’s probably easier to see this in action, so I’ve put together a demo of a Timer
component. Each time that the displayed time changes, the timer calls its onChange
callback — which updates the value of the box at the bottom of the page.
import React from 'react'
import ReactDOM from 'react-dom'
import { TimerDisplay } from './TimerDisplay'
class Timer extends React.Component {
constructor() {
super()
this.state = {
time: 0,
}
this.start = this.start.bind(this)
this.stop = this.stop.bind(this)
this.reset = this.reset.bind(this)
}
render() {
return React.createElement(
TimerDisplay,
{
title: 'POMODORO',
time: this.getTimeRemaining(),
active: this.state.active,
onStart: this.start,
onStop: this.stop,
onReset: this.reset,
}
)
}
getTimeRemaining() {
return Math.max((60*25 - this.state.time)/60, 0)
}
start() {
this.setState({
lastTime: Date.now(),
active: setInterval(() => {
let now = Date.now()
let newTime =
this.state.time +
(now - this.state.lastTime) / 1000
this.setState({
time: newTime,
lastTime: now
})
// If the Timer element receives an `onChange` prop, then
// call it each time the timer's current reading changes.
if (this.props.onChange) {
this.props.onChange(this.getTimeRemaining())
}
}, 37)
})
}
stop() {
clearInterval(this.state.active)
this.setState({
active: null,
})
}
reset() {
this.setState({
time: 0,
})
if (this.props.onChange) {
this.props.onChange(25)
}
}
}
ReactDOM.render(
<Timer onChange={(time) => {
document.getElementById('latestTime').innerHTML =
parseInt(time*100)/100
}} />,
document.getElementById('root')
)
#Now it’s your turn
To get the contact details out of your ContactForm
element, it’s going to need an add button and a callback prop — let’s call it onAddContact
.
When the add button is clicked, the form will need to call the callback prop, with an object containing the current name
and email
.
I’ve added an event handler that logs the added contact details to the console.
Your task is to add the add button (how meta!), and hook it up to the callback prop.
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOM from 'react-dom'
import './styles.css'
class ContactForm extends React.Component {
constructor() {
super()
this.state = {
name: "",
email: "",
}
this.handleChangeName = this.handleChangeName.bind(this)
this.handleChangeEmail = this.handleChangeEmail.bind(this)
}
render() {
return (
<div className='ContactForm'>
<label>
<span>Name</span>
<input
value={this.state.name}
onChange={this.handleChangeName}
/>
</label>
<label>
<span>E-mail</span>
<input
value={this.state.email}
onChange={this.handleChangeEmail}
/>
</label>
</div>
)
}
handleChangeName(event) {
this.setState({
name: event.target.value,
})
}
handleChangeEmail(event) {
this.setState({
email: event.target.value,
})
}
}
ContactForm.propTypes = {
onAddContact: PropTypes.func.isRequired,
}
ReactDOM.render(
<ContactForm
onAddContact={(contact) => console.log(contact)}
/>,
document.getElementById('root')
)
As with the previous exercise, this one is something you might see in a real app — so it’s worth giving it a solid try before taking a look at the solution. And if you do need the solution, make sure to finish off your code afterwards!
And once you’re done, all that’s left is to hook up your form with your list. You’re nearly there!