Watch on YouTubeThe player loads when you press play.

In this video

I start with one counter and a setter that triggers a render. For the counter to work, its value has to survive that render. Adding a second counter introduces a different problem. How does each setter know which value to update?

In this version of useState, I keep the values in an array. Each call takes the next slot, and its setter remembers that slot even after the render finishes. If the hooks change order, a counter starts reading the other counter’s value. The example only handles one component, but you can see why React cares about hook order.

Get the starter code.

Read the automatic transcript

YouTube generated these captions and sometimes gets names, punctuation, and code wrong. Check the recording if something reads oddly.

0:00 — have you ever wondered how exactly react hooks work under the hood and why they have the rules that they have well in this video we're going to answer those questions by building our own used state hook completely from scratch and just before we get started if you do find this video helpful please do hit that like button it really does help me as a creator who's just starting out on youtube so without further ado let's dive in so to get started clone the repo in the link down below if you want to follow along and let's get going so what we have on the page are these

0:31 — two counts over here count a and count b and when i click on add or subtract underneath count a it should change this number over here and with count b over here same thing with these buttons so let's start by creating our use state function so we're going to say const use state is equal to this function and our use state takes in an initial value right so let's take in initial value as an argument so what are we going to return from our use state well we're going to return

1:02 — a value and for now let's just return initial value and then we're also going to return a set function so that'll be set value and we're going to create that set value function here so let's say set value there we go and in here let's just console.log and then bring in new value as an argument so let's just log that out and now we can use our use state so let's say const and then it'll be count a and set count a and that'll equal to our use

1:35 — state call and then we're just going to pass in the initial value and the initial value for count a is going to be one right so now in theory just because we're returning initial value count a should be equal to one right so let's actually bring it in over here this value so let's say count a and save it and let's refresh and you can see that count a is indeed one but what we actually want to do is we wanted to have some sort of persistence right so when we call it multiple times throughout multiple renders we always wanted to return the same state value so what we're going

2:06 — to do is create a new variable outside of our use state function let's call it state value and we're just going to set it to be undefined for now and what we're going to do in our use state hook firstly is we're going to say if our state value is undefined then we're going to set our state value to our initial value right and then instead of returning our initial value down here we're going to return the state value and this state value is now going to store the actual

2:36 — state for our use state so if i save that and refresh you can see everything still works as normal which is to be expected so now let's actually hook up our set count a so we're going to add an on click to our subtract button over here and we're gonna set count a to count a minus one and then i'm just gonna copy that and add it to the add button and this is gonna be exactly the same but just plus one right so if we save this and refresh and i click add you'll see that our set value over here

3:07 — is called with new value and the new value is going to be 2 because we're adding and then if i refresh again and i subtract the new value is going to be 0 because we're going from one minus one to zero perfect so now what we actually wanna do when we call set value is we wanna change this value of state value so pretty simple we just go state value is gonna be equal to our new value right so now in theory we're calling set value and let's say we're setting this from

3:37 — one to zero then we should be returning zero over here instead of our initial state which was one right let's try it out so i click subtract which should set it to zero but nothing happens here and that's because we need to tell react to actually re-render our app component so how we're going to do this is simply just call this reactdom.render function again so right now we call this once when the script is first evaluated but i'm actually going to turn this into

4:07 — a function so i'm going to create a function i'm going to call it render app and i'm going to call it once initially when we first evaluate the script and if we refresh everything should still work as normal but what we're going to do now is when we call set value over here i'm actually going to take a render app and call it after we've set state value to our new value let's refresh and see if that works there we go we've just implemented a

4:39 — very very simple use state from scratch so let's just quickly run through how this works again so we have our use state which gets called as a function inside of our component and it gets and we get our state value back which is count a and we pass that into our different elements and functions down here and then we have our set function as well which is set value up here and when our count value is say one over here and we click subtract

5:10 — then we're just calling set count a one minus one which evaluates to zero that gets passed up to our set value over here so zero comes in here and then we set state value to zero and then we re-render the app so that our app component over here gets called again and when it gets called again our use state gets called again which returns our new state value right so how do we do this for count b as well well normally with a react use state we would bring in

5:42 — count b set count b and have a separate use state call over here and we're going to set this initial state to minus 1. so let's hook that up so we're going to say count b over here so let's pull that in and then for add and subtract i'm just going to copy the add and subtract from count a paste it in and then just going to say count b for these two and then again count b so it's the same thing but just a second time over right so now if i refresh you can see something unexpected or i guess

6:13 — expected happens both count a and b have the same value and if i add and subtract no matter which one i click they both change and that's because we're only storing the state value once right so only the value for this use state over here is actually being stored so how do we store multiple state values for multiple hooks so the way that we're going to do it and this is the way react does it as well is we're going to change state value over here to an array so we're going to change it to cost because we're never

6:43 — going to reassign it so we're going to say state values instead of state value and that's just going to be an empty array right and what we want to do here is we want to store the state for count a at index 0 of the array and for count b we want to store its state at index 1 like this but you might ask how do we know when we call this use state which one we're actually calling well one thing that we could do is i guess we could pass in an index over here or some

7:14 — sort of label right imagine we cast in a label like this saying this is count a state and this is count b state and we then actually stored it as an object over here so a would be one for example and b would be minus one and we would change it like that so we would instead of saying state value over here we'd say state values and then we would pass in our id that we passed in but that's not how react's use state works we don't have to assign any labels at all to our hooks rather react relies on the order that

7:44 — the hooks are called in to actually figure out which state belongs to which hook so let's actually look at how we're going to implement that so i'm going to remove our values over here it's just that it's an empty array and i'm going to create a new variable here called call index and this is the value that's going to track which hook is actually being called so what we're going to do is when use state is called we're going to increment call index by one so let's just do that and then what we're going to do is instead of setting state value over here

8:15 — we're going to change all three of these instances to state values and then pass in that call index so when you state over here is called the first time in a render call index over here is going to be zero but then when the second use state is called right after that call index is going to be one because it gets called again and then just one thing we need to do is create a new variable called current call index and that's going to take in call index and we're going to pass it to number

8:46 — and we're going to use current call index instead of call index here and i'll explain why exactly that is in a bit but let's save this for now and refresh the app and see what happens ah now you can see that count a's value is one and count b's value is minus one that's awesome now in theory everything should work just fine so if i click add on count a oh wait it actually doesn't work so what's actually happening here is when we set the value of count a we're calling rerender app but what happens when we call re-render app

9:17 — is call index by this point has already been incremented twice so what we actually need to do is we need to take call index and we need to reset it whenever we re-render so we're going to set call index back to -1 whenever we re-render the app so let's save that and refresh and see what happens and as you can see everything works beautifully now we can have multiple hooks inside of the component and all of their states are stored here according to their call index or their

9:47 — call order and this basically gives you the same functionality and api as a react use state not just one thing is why we have this current call index so the reason we need to do this is we actually need to kind of freeze our call index for use in this function because what would happen if we have these two hooks is this first use state gets called right and then call index gets set to zero right and then we have our set value over here and that would use call index which at that point would be equal to

10:18 — zero but then we call our second hook over here during render and then call index is going to be one but what actually happens there is because we're referencing call index directly when we call set value on our first hook over here call index at that point because both of them have been called will be one so in order to kind of freeze our call index within this particular use state and make sure that for example for this first call it's always going to be zero we create a separate variable that's frozen and we

10:48 — pass call index into number and the reason we wrap it in number is to make sure that we don't get a pointer to call index overview to make sure that it's a whole new number i haven't seen this happen in brave browser but i remember seeing this when i ran this in a code sandbox ages ago so i just added this back to be safe so obviously this is a super simple implementation of use state and the way that react does it is obviously way more complex and this one for example wouldn't be reliable to use within multiple components it actually just works when

11:18 — you use it in one component but it does highlight a few things about hooks number one just the way it works right how you can have a function that somehow magically contains some state but you know when you actually look at it it's not that magical because we're just storing uh a value in an array right and secondly it highlights why those rules of hooks exist so if we go to uh rules of hooks if we just google that and head to the react documentation you'll see that we have two rules of

11:48 — hooks the one is only call hooks at the top level so don't call them inside loops conditions or nested functions and the second one is only call them from react functions so let's look at the first rule don't call them inside loops conditions or nested functions well if we call them inside of these then we aren't guaranteeing the exact same call order each time because let's say we had it in a loop and that loop iterated over an array that had some sort of length that changed between renders then that hook might have a different call order the next render around

12:19 — and that would mess up our whole storing by call order over here and then secondly only calling react hooks from react functions and that's basically because when you call a hook this hook state actually gets attached to the component itself i mean in our example we're just saving it in the top level of the file because we just wanted a simple implementation but it actually gets stored on that instance of the component within the react virtual dom and calling it from a javascript function just won't work

12:49 — so i hope this has helped you better understand how react hooks work and if you're interested in learning more about the internals of react i suggest sean wang's excellent lesson on rebuilding react from scratch it taught me so much about how react really works and especially when it comes to concurrent mode and suspense and finally please smash that like button if you found this helpful and if you have any questions comment down below and i'll do my best to get back to you finally if you want to know as soon as i release more tutorials like this click the link down below to sign up for my newsletter thanks for watching

13:20 — and i'll catch you in the next one

Originally published on Jacques Blom.