For small apps with one or two pieces of variable data, it’s easy enough to store the data at the top level, and pass it to child components via props — just as we’ve done with the fractal tree examples. The thing is, as apps grow, so does the amount of state that needs to be stored. For example, just on the page you’re currently looking at, there’s state for:
Storing all of this state at the top level and then passing it down via props would be a nightmare. Luckily, you don’t have to, because you can use React’s
useState() function instead!
There’s something I want to get out of the way before diving into any details on
useState(). The thing is, when you see it in the wild, it’ll usually look something like this:
let [name, setName] = useState('')
So here’s my question for you:
What does the syntax on the left hand side of the above
useState() call do?
The above example is just using ES6 Destructuring Assignment. It could be rewritten as so:
let state = useState(1) let name = state let setName = state
So in the above example, here’s what happens:
Naturally, you can name the two destructured variables whatever you like. In this case they’re
setName, but they could just as easily be
setMousePosition. So with that out of the way, it’s time to explore the magical world of state!
useState() do? Simple! In fact, the answer is so simple, that I think you’ll be able to answer it yourself after taking a look at the example below.
Have a play around with the above demo and see if you can figure out what
useState() does. Then, once you have an answer, check it in the box below.
In a nutshell, the
useState() function lets you store state between renders. It returns an array that holds two values — let’s call them
statevariable starts with whatever value is passed to
useState()itself, and remembers its value between renders.
setStatevariable holds a function. You can call this function to set a new value for the state — and if the new value differs from the previous one, the component will be re-rendered.
The best way to really understand
useState() is to give it a try. So, let’s make a small change to the swaying tree:
You task is to update this demo so that the mouse position is stored with component state instead of global state.
To start, you’ll need to add a top level component to store the state (as
useState() can only be called within components). Then, you’ll need to update the mousemove event handler and actually render the state returned by
useState(). Here’s the signature of
let [state, setState] = useState(initialState)
For the moment, don’t touch anything other than the mouse position — we’ll refactor the rest later in this section.
How’d you go? It’s important to give this exercise a try, as
useState() is one of the foundations of real world React apps. The more you use it, the more it’ll start to make sense. And once you’ve given the exercise a go, let’s discuss what’s happening in a little more detail.
In the “How React Works” section, I mentioned that there are two ways to trigger a re-render — and now you’ve seen both of them!
So which one should you use? That’s easy:
Where possible, use component state.
The thing about component state is that it’s stored alongside the components that actually use it. This is great for performance:
setState()calls into a single update to improve performance.
The performance improvement enabled by component state is impressive, and is one of the key ideas that makes React possible. But quite aside from the performance benefit, there’s another major reason to use component state.
Say you want to make a datepicker, a modal, or a dropdown component. But actually, let’s not do that. Nobody really wants to make another datepicker, modal or dropdown component. And that’s the wonderful thing about state! It makes it possible to encapsulate complex behaviors within reusable, independent components.
The killer feature of component state is that it allows you to encapsulate complex functionality with components.
There’s just one thing… state is necessary for encapsulated functionality, but it is usually not sufficient. To build truly useful components, you also need the ability to declare effects. And happily, hooks make this simple.