State, Lifecycle and Event Handlers

Updated: 03 September 2023

Based on this EdX course

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

1
class Welcome extends React.Component {
2
render() {
3
return <h1>Hello World!</h1>
4
}
5
}
6
7
ReactDOM.render(<Welcome />, document.getElementById('root'))

As expected, we can also add props to these components as with functional components as follows

1
class Welcome extends ReactComponent {
2
render() {
3
return <h1>Message: {this.props.message}</h1>
4
}
5
}
6
7
ReactDOM.render(
8
<Welcome message="Hello World!" />,
9
document.getElementById('root')
10
)

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

1
class Counter extends React.Component{
2
constructor(props){
3
super(props)
4
}
5
render(){
6
return <div>Hello World!</h1>
7
}
8
}

Initial state can be defined as well as updated using the constructor and setState functions respectively

1
class Counter extends React.Component {
2
constructor(props) {
3
super(props)
4
//initial state set up
5
this.state = { message: 'Initial message' }
6
}
7
componentDidMount() {
8
//updating state
9
this.setState({ message: 'New message' })
10
}
11
render() {
12
return <div>Message:{this.state.message}</div>
13
}
14
}

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

1
class Counter extends React.Component{
2
constructor(props){
3
super(props)
4
//initial state set up
5
this.state = {message:"Initial message"}
6
}
7
componentDidMount()
8
//updating state
9
this.setState((prevState, props) => {
10
return {message: prevState.message + '!'}
11
})
12
}
13
render(){
14
return <div>Message:{this.state.message}</div>
15
}
16
}

Future State

Since the state updates asynchronously, we cannot immediately use the new state after calling the setState function

1
//this.state.count is originally 0
2
this.setState({ count: 42 })
3
console.log(this.state.count)
4
//outputs 0 still
1
//this.state.count is originally 0
2
this.setState({count:42}, () = {
3
console.log(this.state.count)
4
//outputs 42
5
})

State is Immutable

State is immutable and hence should not be manipulated directly. For example, we cannot do the following

1
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

  1. constructor(props) is called when a component is initialized. This is only called once
  2. componentWillMount() is called just before a component mounts
  3. render() is called when a component is rendered
  4. componentDidMount() 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

  1. componentWillReceiveProps(nextProps) os called when a component has updated and is receiving new props
  2. shouldComponentUpdate(nextProps, nextState) will decide whether a component should run the componentWillUpdate, render(), and componentDidUpdate functions and must return a boolean
  3. componentWillUpdate(nextProps, nextState) is called when a component is about to be updated
  4. render()
  5. 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

1
<button onClick={clickHandler}>Click Here</button>

Event handlers are defined withing a Class component, this can be done as follows

1
class Counter extends React.Component {
2
constructor(props) {
3
super(props)
4
this.state = {
5
count: 0,
6
}
7
this.clickHandler = this.clickHandler.bind(this)
8
}
9
clickHandler() {
10
this.setState((prevState, props) => {
11
return { count: prevState.count + 1 }
12
})
13
}
14
render() {
15
return <button onClick={this.clickHandler}>{this.state.count}</button>
16
}
17
}
18
19
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

1
this.clickHandler = this.clickHandler.bind(this)

Or with the ES6 arrow function to pass forward the context

1
<button onClick={{() => this.clickHandler()}} >{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

1
class Details extends React.Component {
2
render() {
3
return <h1>{this.props.details}</h1>
4
}
5
}
6
7
class Button extends React.Component {
8
render() {
9
return (
10
<button
11
style={{ color: this.props.active ? 'red' : 'blue' }}
12
onClick={() => {
13
this.props.clickHandler(this.props.id, this.props.name)
14
}}
15
>
16
{this.props.name}
17
</button>
18
)
19
}
20
}
21
22
class App extends React.Component {
23
constructor(props) {
24
super(props)
25
this.state = {
26
activeArray: [0, 0, 0, 0],
27
details: '',
28
}
29
this.clickHandler = this.clickHandler.bind(this)
30
}
31
32
clickHandler(id, details) {
33
var arr = [0, 0, 0, 0]
34
arr[id] = 1
35
this.setState({
36
activeArray: arr,
37
details: details,
38
})
39
console.log(id, details)
40
}
41
42
render() {
43
return (
44
<div>
45
<Button
46
id={0}
47
active={this.state.activeArray[0]}
48
clickHandler={this.clickHandler}
49
name="bob"
50
/>
51
<Button
52
id={1}
53
active={this.state.activeArray[1]}
54
clickHandler={this.clickHandler}
55
name="joe"
56
/>
57
<Button
58
id={2}
59
active={this.state.activeArray[2]}
60
clickHandler={this.clickHandler}
61
name="tree"
62
/>
63
<Button
64
id={3}
65
active={this.state.activeArray[3]}
66
clickHandler={this.clickHandler}
67
name="four"
68
/>
69
<Details details={this.state.details} />
70
</div>
71
)
72
}
73
}
74
75
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