State & Lifecycle
# 04. State & Lifecycle
State is similar to props, but state is private and fully controlled by the component.
State is similar to the data option in Vue.
# Converting a Function Component to a Class Component
Before the useState hook, state was managed through class components?
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world</h1>
<h2>It is {this.props.date.toLocalTimeString()}</h2>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
Each time the component updates, the render method is called, but as long as we render <Clock /> into the same DOM node, only a single instance of the Clock class will be created and used. This lets us use additional features such as state and lifecycle methods.
Singleton pattern?
# Adding Local State to a Class Component
class Clock extends React.Component {
// Step 2: Add a constructor and assign initial value to this.state
constructor(props) {
super(props) // Pass props to the parent class constructor via super
this.state = {date: new Date()}
}
render() {
// Step 1: Replace this.props with this.state in the render method
return (
<div>
<h1>Hello, world</h1>
<h2>It is {this.state.date.toLocalTimeString()}</h2>
</div>
)
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Adding Lifecycle Methods to a Class
In applications with many components, it's very important to free up resources taken by the components when they are destroyed.
When the Clock component is first rendered to the DOM, we want to set up a timer (opens new window). This is called "mounting" in React.
Likewise, when the DOM produced by the Clock is removed, we want to clear that timer (opens new window). This is called "unmounting" in React.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
// Mount - runs after mounting is complete
componentDidMount() {
// Start a timer after the component mounts
this.timerID = serInterval( // You can add arbitrary property fields to this
() => this.tick(), 1000
)
}
// Unmount
componentWillUnmount() {
// Clear the timer after the component unmounts to free memory
clearInterval(this.timerID)
}
tick() {
this.setState({
date: new Date()
})
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Using State Correctly
There are three things you should know about setState():
# Do Not Modify State Directly
// Wrong: do not modify state directly
this.state.comment = 'Hello'
// Correct: use setState()
this.setState({comment: 'Hello'})
2
3
4
5
6
The constructor is the only place where you can directly assign to this.state.
Is setState a React internal method that makes state updates reactive?
# State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
To fix this, use a form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
2
3
4
# State Updates are Merged
When you call setState(), React merges the object you provide into the current state.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
// Calling setState separately to update individual state properties
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
The merging is shallow, so this.setState({comments}) leaves this.state.posts intact, but completely replaces this.state.comments.
# The Data Flows Down (Unidirectional Data Flow)
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn't care whether it is defined as a function or a class.
This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
A component may choose to pass its state down as props to its child components:
<FormattedDate date={this.state.date} />
The FormattedDate component would receive the date in its props and wouldn't know whether it came from the Clock's state, from the Clock's props, or was typed by hand.
This is commonly called a "top-down" or "unidirectional" data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components "below" them in the tree.
If you imagine a component tree as a waterfall of props, each component's state is like an additional water source that joins it at an arbitrary point but also flows down.