8

Dealing with State in React

Up to this point, the components we’ve created have been stateless. They have properties (a.k.a. props) that are passed in from their parent, but nothing (usually) changes about them once the components come alive. Your properties are considered immutable once they’ve been set. For many interactive scenarios, you don’t want that. You want to be able to change aspects of your components as a result of some user interaction (or some data getting returned from a server or a billion other things).

We need another way to store data on a component that goes beyond properties. We need a way to store data that can be changed. What we need is something known as state. In this chapter, you learn all about state and how you can use it to create stateful components.

Using State

If you know how to work with properties, you totally know how to work with states…sort of. There are some differences, but they’re too subtle to bore you with right now. Instead, let’s just jump right in and see states in action by using them in a small example.

We’re going to create a simple lightning counter example, as shown in Figure 8.1.

A counter app for the number of lightning strikes is shown. In the figure, a text reads "4,800 Lightning Strikes Worldwide (since you loaded this example)."

Figure 8.1 The app you will be building.

This example does nothing crazy. Lightning strikes Earth’s surface about 100 times a second, according to National Geographic. We have a counter that simply increments a number you see by that same amount. Let’s create it.

Our Starting Point

The primary focus of this example is to see how we can work with state. There’s no point spending a lot of time creating the example from scratch and retracing paths that we’ve walked many times already. That’s not the best use of anybody’s time.

Instead of starting from scratch, modify an existing HTML document or create a new one with the following contents:

Now let’s take a few minutes to look at what our existing code does. First, we have a component called LightningCounterDisplay. The bulk of this component is the divStyle object, which contains the styling information responsible for the cool rounded background. The return function returns a div element that wraps the LightningCounter component.

The LightningCounter component is where all the action will take place:

As it is right now, this component has nothing interesting going for it. It just returns the word Hello! That’s okay—we’ll fix up this component later.

The last thing to look at is our ReactDOM.render method:

It just pushes the LightningCounterDisplay component to our container element in our DOM. That’s pretty much it. The end result is the combination of markup from our ReactDOM.render method and the LightningCounterDisplay and LightningCounter components.

Getting Our Counter On

Now that you have an idea of what we’re starting with, it’s time to make plans for our next steps. The way our counter works is pretty simple. We’re going to be using a setInterval function that calls some code every 1000 milliseconds (a.k.a. 1 second). That “some code” is going to increment a value by 100 each time it’s called. Seems pretty straightforward, right?

To make this all work, we’re relying on three APIs that our React component exposes:

1. componentDidMount

This method gets called just after our component gets rendered (or mounted, as React calls it).

2. setState

This method allows you to update the value of the state object.

You’ll see these APIs in use shortly, but here you get a preview so that you can spot them easily in a lineup.

Setting the Initial State Value

We need a variable to act as our counter. Let’s call this variable strikes. We have a bunch of ways to create this variable, but the most obvious one is the following:

We don’t want to do that, though. For our example, the strikes variable is part of our component’s state. We want to create a state object, make our strikes variable a property of it, and ensure that we set all of this up when our component is getting created. The component we want to do all this to is LightningCounter. Go ahead and add the following highlighted lines:

We specify our state object inside our LightningCounter component’s constructor. This runs way before your component gets rendered. We’re telling React to set an object containing our strikes property (initialized to 0).

If we inspect the value of our state object after this code has run, it looks something like the following:

Before we wrap up this section up, let’s visualize our strikes property. In our render method, make the following highlighted change:

We’ve replaced our default Hello! text with an expression that displays the value stored by the this.state.strikes property. If you preview your example in the browser, you will see a value of 0 displayed. That’s a start!

Starting Our Timer and Setting State

Next up, is getting our timer going and incrementing our strikes property. As we mentioned earlier, we will be using the setInterval function to increase the strikes property by 100 every second. We’re going to do all of this immediately after our component has been rendered using the built-in componentDidMount method.

The code for kicking off our timer looks as follows:

Go ahead and add these highlighted lines to our example. Inside our componentDidMount method that gets called after our component gets rendered, we have our setInterval method that calls a timerTick function every second (or 1000 milliseconds).

We haven’t defined our timerTick function, so let’s fix that by adding the following highlighted lines to our code:

What our timerTick function does is pretty simple: It just calls setState. The setState method comes in various flavors, but for what we’re doing here, it just takes an object as its argument. This object contains all the properties you want to merge into the state object. In our case, we are specifying the strikes property and setting its value to be 100 more than what it is currently.

Note: Incrementing the Existing State Value

As you’ve seen here, you will often end up modifying an existing state value with an updated value. We’re getting the existing state value by calling this.state.strikes. For performance-related reasons, React might decide to batch state updates in rapid succession. This could lead to the original value stored by this.state to be out-of-sync with reality. To help with this, the setState method gives you access to the previous state object via the prevState argument.

Using that argument, our code could be made to look as follows:

The end result is similar to what we had originally. Our strikes property is incremented by 100. The only potential change is that the value of the strikes property is guaranteed to be whatever the earlier value stored by our state object would be.

So should you use this approach to update your state? There are good arguments on both sides. One side argues for correctness, despite this.state working out fine for most real-world cases. The other side argues for keeping the code simple and not introducing additional complexity. There’s no right or wrong answer here, so use whatever approach you prefer. I’m calling this out only for completeness because you could run into the prevState approach in any React code you encounter in the wild.

You need to do one more thing. The timerTick function has been added to our component, but its contents don’t have their context set to our component. In other words, the this keyword where we are accessing setState will return a TypeError in the current situation. You can employ several solutions here, each a little frustrating in its own way. We’ll look at this problem in detail later. For now, we’re going to explicitly bind our timerTick function to our component so that all the this references resolve properly. Add the following line to our constructor:

When you’ve done this, the timerTick function is ready to be a useful part of our component.

Rendering the State Change

If you preview your app now, you’ll see our strikes value start to increment by 100 every second (see Figure 8.2).

The preview of the app screen displays the number 600.

Figure 8.2 The strikes value increments by 100 every second.

Let’s ignore for a moment what happens with our code. That’s pretty straightforward. The interesting thing is that everything we’ve done ends up updating what you see onscreen. That updating has to do with this React behavior: Whenever you call setState and update something in the state object, your component’s render method gets automatically called. This kicks off a cascade of render calls for any component whose output is also affected. The end result of all this is that what you see on your screen in the latest representation of your app’s UI state. Keeping your data and UI in sync is one of the hardest problems with UI development, so it’s nice that React takes care of this for us. It makes all this pain of learning to use React totally worth it…almost!

Optional: The Full Code

What we have right now is just a counter that increments by 100 every second. Nothing about it screams lightning counter, but it does cover everything about states that I wanted you to learn right now. If you want to optionally flesh out your example to look like our version that you saw at the beginning, this is the full code for what goes inside our script tag:

If you make your code look like everything you see here and run the example again, you will see our lightning counter example in all its cyan-colored glory. While you’re at it, take a moment to look through the code to ensure that you don’t see too many surprises.

Conclusion

We just scratched the surface on what we can do to create stateful components. While using a timer to update something in our state object is cool, the real action happens when we start combining user interaction with state. So far, we’ve shied away from the large amount of mouse, touch, keyboard, and other related things that your components will come into contact with. In an upcoming chapter, we fix that. Along the way, you’ll see us taking what we’ve seen about states to a whole new level. If that doesn’t excite you, then I don’t know what will.

Note: If you run into any issues, ask!

If you have any questions or your code isn’t running like you expect, don’t hesitate to ask! Post on the forums at https://forum.kirupa.com and get help from some of the friendliest and most knowledgeable people the Internet has ever brought together!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.154.103