React Hooks — useState
What is a Hook? A Hook is a special function that lets you “hook into” React features.¹
Ever gotten halfway or more through a Functional component or had to jump back into coding a component from days ago and thought…’Urghhh! I need this component to actually have state!’ What you thought had been stateless and perfect now needed to use a lifecycle method like componentDidMount or componentDidUpdate (these methods can be achieved with the useEffect hook…that hook’s for another blog), but going back and changing how you were rendering or maybe you’ve called props a number of places and…oh man, now you need to go back and change the entire component to be a Class component. A silent ‘noooo!’ escapes from you…and we’ve all been there.
With useState, we can actually call state without having to change the component to a class component. Because there is no constructor in a Functional component, in turn, there is no this.state(really, there actually is no this at all in these components). useState allows us to declare a state variable.
Two important things to know about useState: 1. hooks like useState CAN NOT be used in Class components. 2. our useState(s), because we can and should use multiple, must always execute in the same order. useState CAN NOT be used in loops, conditions or nested functions, React and lint will throw an error immediately if you try to code them conditionally, etc.
Let’s walk through a classic counter example. We can build a quick React app and actually get it functioning directly in the app.js file (keeping it a Functional component): In your local environment, in your terminal run: npx create-react-app <your-app-name-here> Next, cd into your app and open the new app with your text editor. Running npm install and npm start come in handy here to open your file in localhost and view changes as well as testing our buttons. I like to immediately strip down my app removing all unnecessary files so the file structure, app.js and index.js files look similar to this:
It was also recently brought to my intention how important it is, even when working on quick, small apps to make sure to clean out your stylesheets, as well, and only leave css that you are actually going to use. (This may seem obvious, but sometimes, in a hurry, we all forget the little things)
Back to our App.js: First, lets build our div, creating 2 buttons, a button that will be responsible for subtracting from our count and a button that will add to our count. In between these two buttons, we’ll need an element (in this case a <span>) that will be responsible for rendering our actual count. Below, the text in the span is hardcoded, but we’ll need to change this to render dynamically.
We’ll need to import useState from react at the top of our file. In the body of our function, the variable of count will become our dynamic value of the state of count. useState is given an argument of the initial state that count is set to. Here we will start our count at 0. useState returns an array of two values, always. The first value is the current state (for us it’s ‘count’) and the second value is the function to update state (below, I’ve called it startCounter, and you can rename this as you see fit). You will never see the const be the array, it is always this destructured array, with it’s 2 items from useState.
Create the synthetic event of onClick on both of your buttons, and outside of our return, build those functions as below. Make sure to use your variable of state as well as your function to update that state. In the return function it is necessary to now change the hardcoded text of the span element to include your variable wrapped in curly braces (jsx).
The code above, will work, but just like in a Class component, it is not best practice to change state directly. You’ll never know if your functions are working with the most up to date value of state, due to the async nature of changing state. It is imperative to change your functions to work with previousState and only be using the most current value of state. While, we are fixing these lines of code, lets also send useState an anonymous function as an argument with a return value of the initial state, as well (lines 7–9). Doing this will have a big impact on rendering. Instead of running every time the function is called, useState will now only run the 1st time the component renders.
It is possible to send an argument of an object to useState, in the case that we are calling more than one variable, but it’s better practice to call useState again, separately for each variable, and will make it easier for you in regards to the rest of the code. I’ll include pictures of how to send an object to useState and then directly below, a picture of calling useState multiple times, which you will find as more common practice.
Before I sign off…as a quick reminder to myself and anyone else who might need this: Functional components can be written 2 ways:
- React Docs| “Using The State Hook” | https://reactjs.org/docs/hooks-state.html