paint-brush
How to properly define state in React componentsby@eyal-eizenberg
7,068 reads
7,068 reads

How to properly define state in React components

by Eyal EizenbergAugust 2nd, 2018
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

If you have been using <a href="https://hackernoon.com/tagged/react" target="_blank">React</a>, there is a 100% chance that you know the pattern of initializing a component’s state in it’s constructor. However, there have always been several issues with this pattern and now thanks to <a href="https://www.npmjs.com/package/@types/react" target="_blank">Definitely Typed — React</a>, we are ‘forced’ to tackle these issues.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to properly define state in React components
Eyal Eizenberg HackerNoon profile picture

If you have been using React, there is a 100% chance that you know the pattern of initializing a component’s state in it’s constructor. However, there have always been several issues with this pattern and now thanks to Definitely Typed — React, we are ‘forced’ to tackle these issues.

Before we dive in, I will just say that my examples are going to be based on Typescript, however, the same ideas/principals are relevant for Javascript.

Common Component’s State Issue

Let’s start by looking at this simple component, which receives props (a superhero’s name) and has an internal state (the superhero’s health).

Up until recently, if you ran this code you would get no compile-time errors. However, in runtime, you would get an error about accessing a property on null because the state was not defined when we tried to access this.state.health. This can happen when you forget to initialize the state for the component in the constructor. Thankfully, the latest version of Definitely Typed @types/react, you will now throw an error on compiling.

Ok, let’s set that state in the constructor!

What’s the problem with using the constructor?

Here is the same component as before, but now we are initializing the state in the constructor.

There are 2 issues with this code:

  1. Unnecessary boilerplate — We are calling the constructor function, defining the prop’s type again and calling super(props). All this just because we want to initialize the state…
  2. We are leaving the state open to mutations — The first rule of React is we do not talk about mutations. The second rule of React is WE DO NOT TALK ABOUT MUTATIONS. Ok, we do talk about it, but as something to avoid at all costs. In this scenario, I can do the following without getting an error: this.state.health = 90; Indeed, my component won’t render again and I will still see the health as 100, but no errors will be thrown since we did not protect the state attribute of the class.

And indeed, starting from @types/react 16.4.3 this code will throw the following error:

Cannot assign to ‘state’ because it is a constant or a read-only property

OK, I’m convinced, what’s the solution?

Luckily, in Typescript we can use the readonly attribute on a class property. This gives us a clean and clear way of protecting the state object from mutations (line 10):

I know what you’re thinking

After reading the example above, the first thought that ran through your head was: “Nice!”. The second thought that ran through your head was: “But what if I need to initialize my state with some logic from the props?!”. Well, there is a nice solution to this:

As you can see in line 12, we are calling the getInitialHealth (which is a pure function) with the component’s props and if the superhero’s name is Spiderman, his health is 0, if it’s someone else, it will be 100. I really like this approach since you are only referencing props because you actually need them, not because the constructor function demands them.

I don’t have time for this! QUICK FIX ASAP!

There is a good chance that you now have bunch of failing projects that need fixing. If you are really short on time (even though these fixes are really small and don’t take a lot of time to change), there are 2 things you can do. Either:

Pin the @types/react version package in your package.json to 16.4.2 before these new typings were introduced.

OR

Define the state property in the class so the compiler ‘knows’ that it is going to be set in the constructor (line 10):

Wrap Up

The ‘new’ way of setting the state in a Typescript React component is really nice and protects your state object from mutations. Sure, this might not be ‘critical’ for expert devs (we are all still humans, right?), but for new devs this can definitely help prevent common mistakes which are usually pretty hard to find.

I have been using Typescript for the past year and everyday I feel like it’s even more of a ‘must have tool’ than ever when developing Javascript.

If you liked this post, please follow me on Medium and show some love by clicking on the ‘Clap’ icon several times. You can also follow me on Twitter and Github.