State, Lifecycle and Event Handlers
React Components
Class Components
React components can also be written as ES6 classes instead of functions. This can be done by extending the React.Component
class
class Welcome extends React.Component {
render(){
return <h1>Hello World!</h1>
}
}
ReactDOM.render(
<Welcome/>,
document.getElementById("root")
)
As expected, we can also add props to these components as with functional components as follows
class Welcome extends ReactComponent {
render(){
return <h1>Message: {this.props.message}</h1>
}
}
ReactDOM.render(
<Welcome message="Hello World!"/>,
document.getElementById("root")
)
State
The constructor
is called before a React component is mounted and is used to set up the initial component state. It is important to call the super(props)
function otherwise the constructor may not work correctly
class Counter extends React.Component{
constructor(props){
super(props)
}
render(){
return <div>Hello World!</h1>
}
}
Initial state can be defined as well as updated using the constructor
and setState
functions respectively
class Counter extends React.Component{
constructor(props){
super(props)
//initial state set up
this.state = {message:"Initial message"}
}
componentDidMount(){
//updating state
this.setState({message:"New message"})
}
render(){
return <div>Message:{this.state.message}</div>
}
}
Previous State
The setState
will update the component when React reaches it in the update queue in order to be more efficient. The method updates the state asynchronously and has a componentDidMount
method that is called when that happens, thereby allowing us to update a component based on previous state
We can use the setState
function with a function that takes two inputs prevState, props
in order to update the properties based on that function
class Counter extends React.Component{
constructor(props){
super(props)
//initial state set up
this.state = {message:"Initial message"}
}
componentDidMount()
//updating state
this.setState((prevState, props) => {
return {message: prevState.message + '!'}
})
}
render(){
return <div>Message:{this.state.message}</div>
}
}
Future State
Since the state updates asynchronously, we cannot immediately use the new state after calling the setState
function
//this.state.count is originally 0
this.setState({count:42})
console.log(this.state.count)
//outputs 0 still
//this.state.count is originally 0
this.setState({count:42}, () = {
console.log(this.state.count)
//outputs 42
})
State is Immutable
State is immutable and hence should not be manipulated directly. For example, we cannot do the following
this.state.message = "New message"
Lifecycle Methods
Each class component goes through a lifecycle which contains multiple phases and methods that can be defined
Mounting
constructor(props)
is called when a component is initialized. This is only called oncecomponentWillMount()
is called just before a component mountsrender()
is called when a component is renderedcomponentDidMount()
is called when a component has been mounted - we will typically make network requests in this phase
Updating
These methods happen when a component's state changes
componentWillReceiveProps(nextProps)
os called when a component has updated and is receiving new propsshouldComponentUpdate(nextProps, nextState)
will decide whether a component should run thecomponentWillUpdate
,render()
, andcomponentDidUpdate
functions and must return aboolean
componentWillUpdate(nextProps, nextState)
is called when a component is about to be updatedrender()
componentDidUpdate(prevProps, prevState)
is called after a component has updated
Unmounting
The componentWillUNmount()
function is called just before a component is removed from the DOM and is used for any cleanup such as cancelling timers and network requests
Event Handlers
Events are handled similar to the way they are handled in HTML, aside from the fact that they are defined in camelCase and use the {}
instead of ""
when attaching them to an element
<button onClick={clickHandler}>Click Here</button>
Event handlers are defined withing a Class component, this can be done as follows
class Counter extends React.Component {
constructor(props){
super(props)
this.state = {
count: 0
}
this.clickHandler = this.clickHandler.bind(this)
}
clickHandler(){
this.setState((prevState, props)=>{
return {count: prevState.count + 1}
})
}
render(){
return <button onClick={this.clickHandler}>{this.state.count}</button>
}
}
ReactDOM.render(
<Counter/>,
document.getElementById("root")
)
If we need access to the correct this
for an event handler we need to bind
the function, this can be done in two ways
From the constructor
with as above
this.clickHandler = this.clickHandler.bind(this)
Or with the ES6
arrow function to pass forward the context
<button onClick= >{this.state.count}</button>
Passing State to Parents
At times it may be necessary to pass state from a child to a parent in order to change some other state elsewhere (either in the parent or in siblings by way of the parent), this can be done by passing the event handler down to the children components through their props, such as can be seen in the Button
class below which attaches the clickHandler
function defined in the App
class
The app below simply displays buttons and text below, and when a button is clicked the state of siblings as well as the button itself should be updated
class Details extends React.Component {
render(){
return <h1>{this.props.details}</h1>
}
}
class Button extends React.Component {
render(){
return (
<button style= onClick={()=> {this.props.clickHandler(this.props.id,this.props.name)}}>
{this.props.name}
</button>
)
}
}
class App extends React.Component {
constructor(props){
super(props)
this.state={
activeArray:[0,0,0,0],
details:""
}
this.clickHandler=this.clickHandler.bind(this)
}
clickHandler(id,details){
var arr = [0,0,0,0]
arr[id] = 1
this.setState({
activeArray:arr,
details:details
})
console.log(id,details)
}
render(){
return (
<div>
<Button id={0} active={this.state.activeArray[0]} clickHandler={this.clickHandler} name="bob"/>
<Button id={1} active={this.state.activeArray[1]} clickHandler={this.clickHandler} name="joe"/>
<Button id={2} active={this.state.activeArray[2]} clickHandler={this.clickHandler} name="tree"/>
<Button id={3} active={this.state.activeArray[3]} clickHandler={this.clickHandler} name="four"/>
<Details details={this.state.details}/>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById("root")
)
Demo App
We can make a Demo App that makes use of all the above, the code can be found on This CodePen