React State
React views components as state machines. By interacting with the user, different states are achieved, and then the UI is rendered to keep the user interface and data consistent.
In React, you only need to update the component's state, then re-render the user interface based on the new state (without manipulating the DOM).
The following example creates an ES6 class that extends React.Component, using this.state
in the render()
method to modify the current time.
A class constructor is added to initialize this.state
, and class components should always call the base constructor with props
.
React Example
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
Next, we will make the Clock set its own timer and update every second.
Adding Lifecycle Methods to the Class
In applications with many components, it's important to free up resources taken by the components when they are destroyed.
Whenever the Clock component is first loaded into the DOM, we want to generate a timer, which in React is called mounting.
Similarly, whenever the DOM produced by the Clock is removed, we also want to clear the timer, which in React is called unmounting.
We can declare special methods on the component class to run some code when the component mounts or unmounts:
React Example
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
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('example')
);
Example Analysis:
The componentDidMount()
and componentWillUnmount()
methods are called lifecycle hooks.
The componentDidMount()
hook is executed after the component output is inserted into the DOM, allowing us to set a timer on this hook.
this.timerID
is the timer's ID, which we can unmount in the componentWillUnmount()
hook.
Code Execution Order:
When
<Clock />
is passed toReactDOM.render()
, React calls theClock
component's constructor. SinceClock
needs to display the current time, it initializesthis.state
with an object containing the current time. We will update this state later.React then calls the
Clock
component'srender()
method. This is how React learns what should be displayed on the screen, and then React updates the DOM to match theClock
's render output.When the
Clock
's output is inserted into the DOM, React calls thecomponentDidMount()
lifecycle hook. Inside it, theClock
component asks the browser to set a timer to calltick()
every second.Every second the browser calls the
tick()
method, inside which theClock
component schedules a UI update by callingsetState()
with an object containing the current time. Thanks to thesetState()
call, React knows the state has changed and calls therender()
method again to learn what should be on the screen. This time,this.state.date
in therender()
method will be different, and so the render output will include the updated time. React updates the DOM accordingly.If the
Clock
component is ever removed from the DOM, React calls thecomponentWillUnmount()
lifecycle hook so the timer is stopped. The browser calls thetick()
method every second. Within it, theClock
component schedules a UI update by callingsetState()
with an object containing the current time. By callingsetState()
, React knows the state has changed and calls therender()
method again to determine what should be displayed on the screen. This time,this.state.date
in therender()
method will be different, so the render output will include the updated time, and the DOM will be updated accordingly.
Once the Clock
component is removed from the DOM, React will call the componentWillUnmount()
lifecycle hook, and the timer will be cleared.
Data Flows Down
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.
In the following example, the FormattedDate component will receive the date value in its props and won't know whether it came from the Clock's state, from the Clock's props, or was typed manually:
React Example
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
This is commonly called 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.
To show that all components are truly isolated, we can create an App component that renders three Clock
s:
React Example
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
In the example above, each Clock component sets up its own timer and updates independently.
In a React application, whether a component is stateful or stateless is considered an implementation detail of the component that may change over time.
We can use stateless components within stateful components, and vice versa.