Speaking Closures While Primitively Cloning React UseState Hook
In this blog post, I am going to briefly define closure and showcase how React hook UseState works under the hood.
So first what is a closure?
We can think about it just like MDN defines it as the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, closure is when a function is able to remember its lexical scope even when that function is executing outside its lexical scope.
The word “lexical” refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.
This is a simple example of closure from our browser console:
sayMyName()
is initialized.pikachu
is created insidesayMyName()
.pikachuYells()
is initialized insidesayMyName()
.alert(pikachu)
is what we intend to have functionpikachuYells()
execute after we invoke it.- in order for it to execute, it needs to access the variable
pikachu
. Thanks to closurepikachu
is remembered in the whole lexical scope ofsayMyName()
.
result:
Just remember that the concept of closure is only more relevant when a function returns a function. The returned function has access to variables that are not in the global scope, but they solely exist in its closure.
I hope this clarified the fundamentals of closure. Now let’s talk about how useState was programmed using closure.
Why useState?
it is imperative to first import a function from React called useState
.
import React, { useState } from "react";
useState
hook lets us "hook into" React's internal state inside of our function component.
We are not going to get into so much details about the usages, but know that understanding closure and how this special function was built is very critical if you want to use React Hooks without having to worry as much about bugs.
Let’s start our primitive clone:
function useState(initialValue) {
var _val = initialValue
_val
is a local variable created by useState.
function state() {
return _val
}
state
is an inner function, a closure.state()
uses_val
, declared by parent function.
function setState(newVal) {
_val = newVal
}
- Setting
_val
without exposing_val
.
return [state, setState]
}
- Exposing functions for external use.
var [foo, setFoo] = useState(0)
- Using array destructuring .
Here is the full code with some console.log’s to help us showcase the result:
function useState(initialValue) {
var _val = initialValue
function state() {
return _val
}
function setState(newVal) {
_val = newVal
}
return [state, setState]
}
var [foo, setFoo] = useState(0)console.log(foo()) // logs 0 - the initialValue we gave
setFoo(1) // sets _val inside useState's scope
console.log(foo()) // logs 1 - new initialValue, despite exact same call
That was our primitive clone of React’s useState
hook. In our function, there are 2 inner functions, state
and setState
. state
returns a local variable _val
defined above and setState
sets the local variable to the parameter passed into it (i.e. newVal
).
Like the above console.log’s made clear, we are able to access and manipulate (a.k.a. “close over”) the internal variable _val
. They retain access to useState
‘s scope, and that reference is called closure. In the context of React and other frameworks, this looks like state, and that’s exactly what it is.
To conclude, I would like to share with you a very nifty process of thoughts, that I learned at Flatiron School.
As we now know, useState
hook allows our data to be dynamic. Which means, anytime we don’t really need that force of change stimulation within our program, useState
hook becomes useless. Therefore, here are the three questions to ask yourself before choosing to work with state:
- Is the data passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.