When you use React correctly, most components consist of the render
method and event handlers:
class ArticleItem extends React.Component {
handleClick = (e) => {
e.preventDefault();
const { onClick } = this.props;
onClick();
}
render() {
const { name, description, link } = this.props;
return (
<div>
<a href="{link}" onClick={this.handleClick}>{name}</a><br />
<div>{description}</div>
</div>
);
}
}
But not all problems are solved so easily. Imagine a <Clock />
component that simulates a digital clock in hh:mm:ss
format:
class Clock extends React.Component {
render() {
const currentTime = new Date();
return (
<div>{currentTime.toLocaleTimeString()}</div>
);
}
}
This component displays the current time. Now we have to figure out how to update it. A clock, unlike conventional components, does not expect action from the user. They update themselves every second. We end up with a chain: an event happens => the current time changes => React calls render
and changes the DOM.
So, the current time initializes the state:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
render() {
const { date } = this.state;
return (
<div>{date.toLocaleTimeString()}</div>
);
}
}
The component still shows the current time when it is rendered. But it's now ready to change.
Time refers to periodic events for which we use timers. It is fine to use setInterval
for <Clock />
. We should set the timer immediately after rendering the clock. And we should clear it when we remove the component from the item tree:
setInterval(() => this.setState({ date: new Date() }), 1000);
When do we start the timer? We call render
whenever the state changes, so it's not the right option. Because then <Clock />
will start a new timer every second. The constructor seems like a more appropriate place, but there is something else here.
Calling the constructor and rendering the clock in the DOM tree are generally independent events. Look at this code:
// We call the constructor
const clock = <Clock />;
// Something is taking a long time
// Rendering
const root = createRoot(document.getElementById('root'));
root.render(clock);
This clock isn't in the DOM tree yet, but it's already running. Is it worth worrying about? Yes, this behavior is unexpected. It interferes with testing and wastes CPU time. In addition, the constructor doesn't remove the timer in any way.
Each React component goes through several stages in its life: we create it, add it to the DOM, then it receives props, and we finally remove it from the tree. This process is called the Component Lifecycle.
React provides a set of methods to integrate into this process. For example, we can start the clock immediately after rendering it. The componentDidMount
method can help with this. It's called immediately after rendering a component. It happens only once:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
// We save the timer identifier
this.timerId = setInterval(() => this.setState({ date: new Date() }), 1000);
}
render() {
const { date } = this.state;
return (
<div>{date.toLocaleTimeString()}</div>
);
}
}
Note how we store the timer inside the object. It's not involved in the view, so there's no need to use the state.
Now you need to clear the timer. We execute the method componentWillUnmount
just before we remove the component from the DOM:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerId = setInterval(() => this.setState({ date: new Date() }), 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
render() {
const { date } = this.state;
return (
<div>{date.toLocaleTimeString()}</div>
);
}
}
The clock now looks finished.
So, you've learned two methods to build into a component's lifecycle, but there are many more. We can divide them into three independent groups.
Mounting
We call these methods when the object is created and placed into the DOM:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
Updating
Updating can occur when properties or states change. We call these methods during rerendering:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
Unmount
There's one method in this group. We call it when we remove a component from the DOM:
componentWillUnmount()
There are many methods, but only a few are used regularly in real projects. For example, the method componentDidMount
is used pretty often. It helps to set timers, make AJAX requests, and change the DOM to bypass React. The latter is necessary when integrating with third-party libraries.
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.