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?
If you’re not exactly sure, I’ll give you a hint: there’s nothing React-specific about it; it’s just plain old JavaScript. When you’re ready to see the answer, take a look inside the box below.
The above example is just using ES6 Destructuring Assignment. It could be rewritten as so:
let state = useState(1)
let name = state[0]
let setName = state[1]
So in the above example, here’s what happens:
useState()
is called.name
and setName
variables.Naturally, you can name the two destructured variables whatever you like. In this case they’re name
and setName
, but they could just as easily be mousePosition
and setMousePosition
. So with that out of the way, it’s time to explore the magical world of state!
useState()
functionWhat does 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 state
and setState
:
state
variable starts with whatever value is passed to useState()
itself, and remembers its value between renders.setState
variable 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 useState()
again:
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!
ReactDOM.render()
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.