I’m reading this freeCodeCamp article to try and understand the useState React Hook. The following code is included in the article. The part I’m confused about is- how does the computer know what the variable prevCount is if it’s not defined anywhere?
When you defined the function, you said, “hey, if this function gets passed a parameter, I want it to be called ‘prevCount’. I don’t know what it was called before, but whatever comes in as that first parameter, within this function, I’m calling it ‘prevCount’.” And fortunately, when setCount receives that callback function, it takes it, executes it, and passes a parameter. I don’t know what it is called internally to the inner workings of useState and we don’t really care. All we know is that it will pass the current value into that callback function as the first parameter. And then the callback function will grab that first parameter and call it whatever it wants.
Indeed () => setCount(myIncrementCallbackFunction) has no parameters. However it doesn’t need any, because it’s job is to handle the click event. And unless you need extra information about the click event itself, you can happy handle the event with no other information than the fact a click occurred.
This function call setCount and passes in the function. The function at this stage is not being called, it is being passed around, like a variable to the setCount
Inside setCount something happens. What? Well it’s code you don’t control anymore, its the hook code that the React frameworks has provided for you. So what does React do with the function you passed in? It calls it with the current state.
This is finally where we get to prevCount => prevCount + 1, because this is the code it will call. And this code is where prevCount is defined.
For example write a function that takes 3 functions and then passes x through the first, second and third one. Do this using both arrow functions and traditional function() both anonymous and named. Use a debugger when running the code and console.log to see what is going on. Hopefully it will make sense then.
There is a tacit knowledge aspect to this, and practice and struggle might be needed to get it. I have suffered through this myself but in C#, but the concept luckily is similar in JS so I didn’t have to relearn. That’s the good thing about this you can reuse this idea of passing functions in most modern languages.
So setCount gets its argument from the framework (from state).
setCount takes a function as a parameter. (It can also take a value, but let’s not worry about that.) setCount takes that function and passes it a value. That happens internal to setCount and you don’t see it. This is part of functional programing - passing functions around as parameters.
And the value of that state starts at 0 because of what was passed to useState initially.
Yes, because that is the initial value being passed to useState. If you’d done:
okay, interesting. I think I first learned to pass in a value with the useState hooks and only learned to pass in a function without registering that it’s a completely different kind of thing. Duhrrr…
It wouldn’t work because what this would do is call setCount(myIncrementCallbackFunction) at the point of rendering the button. (I imagine it would cause an infinite loop as this will trigger another render after each render). You want it to be called at the right time - i.e. when the button is clicked.
I can’t overstate how eye opening it is to play with theses things in the debugger of Chrome or Firefox dev tools. The learning rate per minute is 10x. Try the code you suggested and then see what happens, if it crashes doesn’t matter you are not going to release that code.
Another thing you might want to try, purely as a learning exercise, is use JS instead of JSX. See https://reactjs.org/docs/react-without-jsx.html because if you do that, you will see what is happening at the JS level. I think JSX is making it less clear in this situation.
This is a common pattern in JS where you have a callback. If your callback takes no parameters, or takes the ones being sent by default by the element, then you can just give the function name:
But if you need to pass a parameter, you can’t just add the parameter:
// this won't work
<button onClick={ myCallbackFunction(myParameter) }>My Button</button>
In the first case we were giving it the name of the function, passing in the reference or the address. In the second case we are actually calling the function at compile time and passing the return value to the onPress. We don’t want that, so we can wrap it in an anonymous function:
Now, we are passing it an function again: () => myCallbackFunction(myParameter). When the click is done, that is the function that will get executed and it will call our callback with the parameter.
This is a patter that pops up a lot with callback functions.
There is also a pattern using currying where you can execute a function immediately there, but let’s not worry about that right now. I just mention it for completeness. It would just be a function that returns the function that we want.