by Jake Wiesler
A quick introduction to the React State Hook
The React Hooks proposal comes with some built-in hooks that focus on doing one thing, such as providing state or context to a function component. You can also use these as building blocks to create your own.
In a recent post, I shared some personal thoughts on the hooks proposal. This post will be more technical, as I go further into detail on what I consider to be the most important hook:
As of writing this, hooks are still an experimental proposal. Nothing in this post should be considered final. Please keep this in mind. There is currently an open RFC where you can stay current on the proposal, and even voice your concerns if you have any.
Hooks are available in
v16.7.0-alpha of React. I’ve set up a CodeSandbox that will get you going quickly if you want to follow along with the example in this post.
React state today
If you want a stateful component in React, your only option at the moment is to write that component as a class. This has been a source of frustration for me. Often I will find myself spending a good chunk of mental energy contemplating whether or not I want to refactor a perfectly acceptable function component into a class merely to hold some state.
I’ll convince myself that avoiding such a refactor is in my best interests. Eventually I’ll find myself falling down a rabbit hole of state management strategies, libraries, and “other solutions”.
If things really go south, I’ll start asking if the component even needs state in the first place, as if it’s something that should be avoided.
It sounds excessive, and it probably is. But it’s happened. And if you’ve spent a significant amount of time working with React, you may have found yourself on this wild goose chase as well (or maybe it’s just me 🤔).
Adding state to a component should feel natural, but it’s hard to feel natural when I’m writing a class.
useState hook, function components can now hold local state.
useState is a function that accepts an initial state and returns an array with 2 items:
- The current state
- A function that can be called to update the state
Because of the way array destructuring works, we can name the items returned by
useState anything we want. There is no constraint imposed on us by the API. As a convention, it seems that the React ecosystem is taking to the
[value, setValue] syntax.
In the example above,
color is the state value and is initialized to
setColor function can be called to update that value.
Note that, unlike a class component, state in a function component does not need to be an object. Here it’s just a string.
Another important note is that the update function, in this case
setColor, does not merge the new state with the current, but instead overrides it completely. This is different from how
this.setState works in class components.
The value of
color will be preserved between re-renders (more on this below), unless the
setColor function is called with a new value:
When the button is clicked, the function
slow will call
setColor with a value of
'YELLOW'. This will cause the
StreetLight component to re-render. When it does, the
color variable will be updated to
At first glance, you would think that every time
StreetLight renders, it calls
useState with a value of
'GREEN'. So how can
color be anything but green?
A logical question. Here are a few lines from the React docs that may help ease you in to this concept:
“Normally, variables ‘disappear’ when the function exits but state variables are preserved by React.”
“React will remember its current value between re-renders, and provide the most recent one to our function.”
“You might be wondering: why is
createStateinstead? ‘Create’ wouldn’t be quite accurate because the state is only created the first time our component renders. During the next renders,
useStategives us the current state.”
Put simply, React keeps track of calls to
useState for each component internally. It will also create a mapping between the update function and the state value for which it updates.
The initial value passed to
useState is returned on the first render, but from there React will return the correct value based on the mapping. It also uses the map to know which slice of state to mutate when the update function is called.
This may seem perplexing to you, and you’re not alone. I was baffled by how this could work as well. My confusion only increased when I found out that you can have multiple calls to
useState in the same component:
Yes, you can do this to your heart’s content.
How does React keep track of the state?
In order for all of this to work, React expects that you follow a few rules:
- Only call hooks at the top level of a function
- Only call hooks from function components and custom hooks
React imposes these rules because it relies on the call order of hooks to manage data properly. This may seem fickle at first, but these rules aren’t hard to follow. And quite frankly I can’t think of a scenario where you’d want to break them.
useState to be a building block. You can use it as-is to provide state to your function components, or you can use it to abstract stateful logic out into custom hooks!
I believe custom hooks are going to be the biggest superpower React developers will gain when
v16.7 lands, and
useState is the foundation. The community is already sharing some awesome stuff with custom hooks and this pattern will only grow exponentially.
I hope you found this article helpful. Please reach out to me on Twitter if you have any questions, and as always, happy coding!
Originally published at www.jakewiesler.com.
Hey friend, thanks for reading! The name’s Jake. I love building user interfaces and write about it every week. I also run a small newsletter called Original Copy. It’s a casual and lighthearted affair. If that sounds like your cup of tea, consider subscribing!