Journey, React

Day 8: What is state, how is different than props?

One thing about this.props is that we don’t have a way of modifying the prop from the component. We didn’t have to do that with our Title header in the previous post. We have to be careful as we don’t want it to be set to some random element by accident. However there are times that we need to have the variable changed, maybe it’s new data coming in from an API or the user requested a change via input. In this case we use what is known as state.

State inside the component is changed by the component. You can think of it as a local variable where it only belongs to the component that is accessing it.  To access the state we use this.state in the component.

When we make changes to a state we do so with this.setState() function.

So many words, let’s see how it all works. 

We want to have a clock component that shows the current time. We could use some creative way to use props to have it update the whole page, but what if other parts of the page didn’t need to rerender it would make things slow over time.

Instead we can just rerender the clock component.

Let’s create our clock component first


import React, { Component } from 'react';
class Clock extends React.Component {
render() {
const currentTime = new Date(),
hours = currentTime.getHours(),
minutes = currentTime.getMinutes(),
seconds = currentTime.getSeconds(),
ampm = hours >= 12? 'pm': 'am';
return(
<div className="clock"> {
hours == 0 ? 12: (hours > 12) ?
hours – 12: hours
}:
{
minutes > 9 ? minutes: `0${minutes}`
}:
{
seconds > 0 ? seconds: `0${seconds}`
}
{ampm}
</div>
)
}
}
export default Clock;

view raw

gistfile1.txt

hosted with ❤ by GitHub

This is the logic behind showing the number. If the numbers are smaller than 10 we need to add a zero to the number. We also need to set the am/pm appropriately.

Right now if we render our clock component all we are going to get is the time when we render it and that’s it, it won’t update. So it’s not very useful yet.

What we need to do is have the clock is update every second, we do this by setting the initial state value.  We can think of this as declaring the variable. Just that when we do this now we are setting the current time into the state. Like a snapshot of it at the moment that the code is run.

This is easily done with ES6 with the constructor() function and doing

constructor(props) {
 super(props);
 const currentTime = new Date()
 this.state = {
   hours: currentTime.getHours(),
   minutes: currentTime.getMinutes(),
   seconds: currentTime.getSeconds(),
   ampm: currentTime.getHours() >= 12 ? 'pm': 'am'
 };
}

The first line of the constructor should always be super(props) otherwise you will get errors.

I had to learn this the hard way I would often forget to do it and then wonder why it wouldn’t work. 

The next lines this.state={…} we moved our logic in here because this is where the initial state is set and we will have it for the rest of the component to use to update itself.

Next we need to make the clock set up a timer and update itself every second.

Let’s update our render () function to grab the values from this.state :

render() {
 const {hours, minutes, seconds, ampm} = this.state;

return(
  
{ hours == 0 ? 12: (hours > 12) ? hours - 12: hours }: { minutes > 9 ? minutes: `0${minutes}` }: { seconds > 0 ? seconds: `0${seconds}` } {ampm}
) }

The only thing we have done so far is to use the same trick from last time to make less typing for ourselves instead of typing this.state.hours we just  have to type hours.

We are going to use the setTimeout() Javascript function to have our clock update itself every second which is the same as 1000 milliseconds.

 

We are going to put this inside a function because we will using it more than once. Let’s call that function setTimer().

For simplicity we will call our setTimer() function in the constructor just to get it started.


import React, { Component } from 'react';
class Clock extends React.Component {
constructor(props) {
super(props);
const currentTime = new Date()
this.state = {
hours: currentTime.getHours(),
minutes: currentTime.getMinutes(),
seconds: currentTime.getSeconds(),
ampm: currentTime.getHours() >= 12 ? 'pm': 'am'
};
this.setTimer();
}
setTimer() {
//will run for 1 second and then call updateClock
setTimeout(this.updateClock.bind(this), 1000);
}
updateClock() {
//After one second it will update the Clock by one second
}
render() {
const {hours, minutes, seconds, ampm} = this.state;
return(
<div className="clock"> {
hours == 0 ? 12: (hours > 12) ?
hours – 12: hours
}:
{
minutes > 9 ? minutes: `0${minutes}`
}:
{
seconds > 0 ? seconds: `0${seconds}`
}
{ampm}
</div>
)
}
}
export default Clock;

In the updateClock() Function we will update the state with the new time

updateClock() {
 //After one second it will update the Clock by one second
 const currentTime = new Date();
 this.setState({
  hours: currentTime.getHours(), 
  minutes: currentTime.getMinutes(),
  seconds: currentTime.getSeconds(), 
  ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
 })
}

This will only work once, because we aren’t calling setTimer() again. So to fix that we will add it to the end of the function.

updateClock() {
  ...
 })
this.setTimer();
}

Now our clock works and it updates every second. However it can happen that the re rendering of the page is slower than the timeout function is being called and it creates a bottleneck. To fix this we can pass this.setTimer as a second argument instead of calling it after this.setState(). 


import React, { Component } from 'react';
class Clock extends React.Component {
constructor(props) {
super(props);
const currentTime = new Date()
this.state = {
hours: currentTime.getHours(),
minutes: currentTime.getMinutes(),
seconds: currentTime.getSeconds(),
ampm: currentTime.getHours() >= 12 ? 'pm': 'am'
};
this.setTimer();
}
setTimer() {
//will run for 1 second and then call updateClock
setTimeout(this.updateClock.bind(this), 1000);
}
updateClock() {
//After one second it will update the Clock by one second
const currentTime = new Date();
this.setState({
hours: currentTime.getHours(),
minutes: currentTime.getMinutes(),
seconds: currentTime.getSeconds(),
ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
}, this.setTimer());
}
render() {
const {hours, minutes, seconds, ampm} = this.state;
return(
<div className="clock"> {
hours == 0 ? 12: (hours > 12) ?
hours – 12: hours
}:
{
minutes > 9 ? minutes: `0${minutes}`
}:
{
seconds > 0 ? seconds: `0${seconds}`
}
{ampm}
</div>
)
}
}
export default Clock;

As you can tell there is a lot more to working with state as opposed to props. State is very useful but for we want to use it sparingly as it can introduce subtle errors in the behavior of rendering our components.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s