State and lifecycle
Based on tutorial
No state
Here we have no state. We call render every second and return a new element from Clock
.
function Clock(props) {
const now = props.date.toLocaleTimeString()
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { now }.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Make stateful component
1. Convert to class
Here we make Clock
responsible to updating itself. Only one instance of Clock
is created and then the render
method is called for UI updates.
We add state to Clock
by making it a class with a single method and use this.props
in place of props
.
class Clock extends React.Component {
render() {
const now = this.props.date.toLocaleTimeString()
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { now }.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock date={ new Date() } />,
document.getElementById('root')
);
2. Add state to class
- Replace
this.props
withthis.state
. - Add a constructor to set the initial state.
In this case, we don’t need any props values to be passed and so let the constructor set the time.
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
render() {
const now = this.state.date.toLocaleTimeString()
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { now }.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Each instance of Clock
has its own independent state.
Sample:
function App() {
return (
<>
<Clock />
<Clock />
<Clock />
</>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
3. Add lifecycle methods
Here free up resources taken by the components when they are destroyed.
On mount, we use componentDidMount
set up the timer.
And on unmount, we use componentWillUnmount
to clear the timer.
NB. Use this.setState
method to change state. Do not change values directly on this.state
except in the constructor.
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { date: new Date() }
}
tick() {
this.setState({ date: new Date() })
}
componentWillMount() {
this.timerId = setInterval(() => this.tick(), 1000)
}
componentWillMount() {
clearInterval(this.timerId)
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
4. Handle asynchronous behavior
React might batch setState
requests for performance.
The values of this.props
and this.state
may be updated asychronously. So this may fail to update correctly.
this.setState({
counter: this.state.counter + this.props.increment,
});
So instead of using those, rather pass a function to setState
. Set state
and props
as parameters then use those.
this.setState((state, props) => {
counter: state.counter + props.increment
})
5. Set multiple variables on the state
When you call this.setState
, values are merged in so only the relevant keys are updated and the rest are left intact.
Here we make two asynchronous (non-blocking) API requests and then set the values in the state whenever they resolve.
class Clock extends React.Component {
constructor() {
this.state = {
posts: [],
comments: []
}
}
componentWillMount() {
fetchPosts()
.then(resp => this.setState({ posts: resp.posts })
fetchComments()
.then(resp => this.setState({ comments: resp.comments })
}
}
6. Data flows down
State should be local or encapsulated. Other components should not be able to see the state of a component.
You can however pass a component’s state down to a child component, which doesn’t care where the data comes from but just finds it on the props.
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
<FormattedDate date={this.state.date} />