How to write React useState()?
Hi there everyone. If you ever have worked with React, most likely you already have used useState()
. You also may have read a lot about react hooks and useState()
, Have you ever thought about writing it yourself? Of course not for using it, but only to understand what’s happening down there.
Today we are going to try to write useState()
ourselves (or something like that). You can use React without knowing any of the things I’m going to tell and this is for the case you like to know more. You have to have a basic understanding of React and Javascript to understand this article better.
Writing stateHook()
Before we go any further, try it yourself. Probably you have used it dozen times, how are you going to write one?
So let's start by specifying what do we want:
const [count , setCount] = useState(0);
We need a function that returns an array containing a variable and a function to change that variable. Don’t forget about the initial value as a parameter. We are going to call this function stateHook()
.
Wow. It looks like we are going a little fast. So let me explain it step by step. As I said earlier stateHook
should return a variable and function, just like useState
. We defined state
and initial
at the start. state
is used to store the value. initial
is a boolean which determines it is initial render or not and we need to assign initialValue
to state
. Then we have stateHook
(why state
and initial
are defined out of stateHook
? we will find out later). In stateHook
first, We check if initial
and if it’s true
we assign initialValue
to state
. We assign initial
to false
obviously, we don’t need initialValue
after the first render. We return state
as the first element of the array. Now we get to checksetState
. value
is the only parameter in setState
and it can be string, object, array, … and function! Yup, it can also be a function. Remember how we used useState()
:
So if value
is a function, the returnee is the new state
and if not, we just assign value
to state
. At the end of stateHook
we return both state
and setState
. We left one question behind. Why state
and initial
are outside of stateHook
? (it smells like closure here!). We have a function that has access to variables out of its scope. Variables and function together form a closure. If we put state
or initial
inside stateHook
, we lost value of state
or initial
after each stateHook
call or in another term every time the component is rendered. We use closure to preserve value between calls.
It seems we have completed something likeuseState()
but does it really work? We are going to try it but the function we use to update value can’t make a component rerender and to fix that I cheated a little bit. Beside stateHook
I used useState
and in render
I change a boolean to make the component rerender. We used render
after setCount
to see count changes.
It seems alright. count
increases and decreases with each click. We defined state
outside of stateHook
to store value and inside stateHook
we have setState to update the value. We are using closure to create a function like useState
.So far so good! but there is a problem with stateHook
can you guess what is it? take a moment to think.
If you add another counter you can see the problem. Both counters have the same value, no matter which decreases or increases you are clicking.
Solving problem
Although we have defined two different variables for counters they are the same and the first initial value is used for both of them. Why is that? because it does not matter how many times we use stateHook
, state
in stateHook
is used for all. initial
value is false after the first call so both of the counters are set to the first initial value. If we want to use it more than once, we need to do some modifications. To start, let’s change state
to an array.
let state = [];
Now we can have more than one value in our state but if we want to use an array we need an index to get and set the right values. Each stateHook
call now has to send a unique index
which is value index inside state
array. keep in mind stateHook
indexes should be the same in each render in order to get and update the correct value. We had something like that in hooks docs:
you ensure that Hooks are called in the same order each time a component renders
Call order is kept byindex
, right?
initial
is removed and we check if state[index]
was undefined
, the initial value would be used.
This is stateHook
now. Does it work?
Conclusion
Passing the correct order is really important. Also number of stateHook
should be the same between renders. That’s why we can’t use stateHook
in if
or loop
. If we use stateHook
inside loop or conditional statement we may lose track of hookOrder
and stateHook
would return some irrelevant value.
I think the rules of hooks will make more sense now! Hooks are also working by call order but it is not handled under the hood and not by passing an index.
Thanks for reading this article. I hope it helped you to understand useState
better. If you looking to read more I suggest this post. It was really instructive and it is the idea behind this article.