Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.
In this article, we’ll look at the lifecycle of React components and how to change their internal state.
Changing Internal State
Props are immutable since we want to keep React components pure with respect to props. This means that when we pass in the same props, we always get the same output assuming nothing else changes.
This isn’t very useful in most cases since we want to change things inside a component.
To do this, we can manipulate the internal state of a component.
All class-based React components have internal state accessible via this.state
, and we can change it by calling this.setState
.
For example, if we want to create a Clock
component that shows the current time and updates it every second, we can do that as follows:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date(), timer: undefined };
}
componentWillMount() {
this.updateDate();
}
componentWillUnmount() {
clearInterval(this.state.timer);
}
updateDate() {
const timer = setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
this.setState({ timer });
}
render() {
return (
<div>
<p>It is {this.state.date.toLocaleTimeString()}.</p>
</div>
);
}
}
In the code above, we declared a class Clock
that extends React.Component
. This means the code is a React component, which can have its own lifecycle methods and the render
method.
Inside the Clock
component, we declared the state
field in the constructor
which has the initial state date
, and a timer
state to hold the timer object that’s returned by setInterval
.
Also, we called super(props)
since Clock
extends React.Component
, and the React.Constructor
takes props
as an argument.
Then we have our componentDidMount
lifecycle hook to load the initialization code when the Clock
component is mounted into the DOM.
The updateDate
method is called in componentDidMount
to continuously update the this.state.date
every second.
To update the date inside the setInterval
callback, we called this.setState
with an object inside.
The object has the state’s name as the key as we defined it in the first line, and the value is the new value that we want to set the state to.
We also called setState
to set the timer
field so that we can call clearInterval
with it inside the componentWillUnmount
. The componentWillUnmount
hook is called when the component is removed from the DOM.
It’s a good place to run any code that’s used to clean things up.
Finally, in the render
method, we render the date. This method is called whenever this.state
changes by calling this.setState
, so if we put this.state
properties in there, the latest value of it will always be shown.
This means that this.state.date
will be the latest date after:
this.setState({ date: new Date() });
is called.
componentDidMount
and componentWillUnmount
are lifecycle methods. They’re called only in specific parts of a React component’s lifecycle.
this.setState
is asynchronous, so we shouldn’t assume that code that’s listed after a this.setstate
call will run immediately after this.setState
.
To mount it in the DOM, we call the ReactDOM.render
method with Clock
passed in as follows:
import React from "react";
import ReactDOM from "react-dom";
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date(), timer: undefined };
}
componentWillMount() {
this.updateDate();
}
componentWillUnmount() {
clearInterval(this.state.timer);
}
updateDate() {
const timer = setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
this.setState({ timer });
}
render() {
return (
<div>
<p>It is {this.state.date.toLocaleTimeString()}.</p>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Clock />, rootElement);
Using State Correctly
We should never modify the state directly because it won’t trigger a re-rendering of a component.
So we shouldn’t write:
this.state.foo = 'bar';
Instead, we should always use this.setState
as follows:
this.setState({foo: 'bar'});
State Updates May Be Asynchronous
State updates are asynchronous, so we shouldn’t depend on it run one after the other.
Also, we can’t just combine props
and state
values within setState
.
For example, the following may fail:
this.setState({
count: this.state.count + this.props.increment,
});
Instead, we write:
this.setState((state, props) => ({
count: state.count + props.increment
}));
to get the latest values from props
to update the code.
Regular and arrow functions both work, so we can also write:
this.setState(function(state, props) {
return {
count: state.count + props.increment
};
});
State Updates are Merged
When we call setState
, React merges the object that we pass into setState
into the current state.
For example, if we have 2 states, firstName
and lastName
, then when we called this.setstate({ lastName });
, this.state.firstName
stays intact, while this.state.lastName
is updated with the latest value of lastName
.
Data Flows Down
States are always inside a component, and it can be passed down to child components and elements as props.
React apps has a unidirectional data flow that goes from parent to child.
Conclusion
We can create a React component that is dynamic by storing states inside a component.
The state object is stored in the state
field of the React component class.
We can set the state by running this.setState
.
React components have lifecycles that trigger methods in the React component class. These are called lifecycle hooks.
We can use them to run code at certain stages of a React component’s lifecycle.