Below is a simple hook
function App() {
let [counter, setCounter] = useState(0);
return <button onClick={() => setCounter(counter + 1)}>{counter}</button>;
}
My understanding on how react works is:
React will call App()
to get a React element as:
let element = App();
and the element can be rendered on the browser, fair enough.
Now I click the button to increment the counter
, here is what I don’t understand:
we all know a fucntion’s local variables (counter, setCounter) get destoryed/discarded when the function finishes the execution and get popped out from the stack, so how can setCounter
and counter
can be accessed again in the callback if those two variables are no longer available?
2
Answers
When the
App()
function is initially called, theuseState(0)
call sets up a closure. A closure allows a function to remember the variables in its scope at the time it was created. In this case, it remembers the counter andsetCounter
variables even after theApp()
function finishes executing.When you click the button and trigger the onClick event, the callback function
() => setCounter(counter + 1)
is executed. Despite the fact that theApp()
function has already completed, the callback function retains access to thecounter
andsetCounter
variables through the closure.Here’s a link for your reference,
Closures in javascript
One suggestion is try not to use state in the child component and instead pass the state from parent to child and update the parent component state from child just like in the example from the reference
Your understanding of how React works is mostly correct, but there’s a bit more going on behind the scenes.
When you call
App()
, it returns a React element, but it doesn’t directly render the element on the browser. Instead, React takes that element and creates a virtual representation of the DOM (Virtual DOM) based on it. This virtual representation is a lightweight copy of the actual DOM tree that React uses to efficiently compute the changes that need to be made.Now, let’s focus on the useState hook in your example. When the component function
App()
is called, React initializes thecounter
state variable to 0 and thesetCounter
function that allows updating the state. These variables are not just regular local variables like you would have in a normal function. Instead, they are part of what we call a "closure."A closure is a function along with its lexical environment, which consists of any variables that were in scope when the function was defined. In other words, the function keeps access to the variables that were available in the scope where it was defined, even if that scope has finished executing and the local variables would usually be discarded.
So when the
onClick
callback is triggered, it is able to access thesetCounter
function because it is part of the closure of theApp()
function. ThesetCounter
function, in turn, has access to thecounter
variable, which it updates when invoked with a new value.In simple terms, you can think of it this way: The
App()
function creates a closure that captures thecounter
state variable and thesetCounter
function. When the button is clicked and the callback is executed, it’s able to access thesetCounter
function, which, in turn, can access and update thecounter
variable, thanks to the closure mechanism.So, even though the local variables of the
App()
function would usually be destroyed after the function finishes executing, React’s closure mechanism ensures that the state and updater functions are preserved and accessible when needed. This allows React to maintain and manage stateful data in functional components effectively.