Closure in React
In JavaScript, a closure is formed when an inner function is returned from an outer function, allowing the inner function to access the outer function's variables and parameters even after the outer function has completed execution. Closures are widely used in React to manage state and implement event handlers.
Here's a simple example of closure in React:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}In this example, useState is a hook that returns an array with two elements: the current state value, and a function to update it. The count variable and the setCount function are declared using destructuring assignment.
The handleClick function is defined inside the Counter function, and it has access to the count and setCount variables through closure. When the user clicks the button, the handleClick function is called, and it updates the state by calling setCount with the new value of count.
Using closure in this way allows us to manage states in a functional component without having to use class components or global variables. It's a powerful tool for building complex UIs in React.
Another use case of closure in React is for creating reusable components with private states.
For example, let's say you want to create a component that displays a list of items and allows the user to add and remove items from the list. You could use closure to keep track of the list of items internally, without exposing it to the outside world.
// App.js
import { useState } from 'react';
import AddItemForm from './AddItemForm'
function List() {
const [items, setItems] = useState([]);
function addItem(item) {
setItems([...items, item]);
}
function removeItem(index) {
// we don't need the item that's why we add _ in the filter first parameter.
setItems(items.filter((_, i) => i !== index));
}
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>
{item} <button onClick={() => removeItem(index)}>Remove</button>
</li>
))}
</ul>
<AddItemForm onAdd={addItem} />
</div>
);
}// AddItemForm.js
function AddItemForm({ onAdd }) {
const [value, setValue] = useState('');
function handleSubmit(event) {
event.preventDefault();
onAdd(value);
setValue('');
}
return (
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={(e) => setValue(e.target.value)} />
<button type="submit">Add</button>
</form>
);
}In this example, the List component declares a state variable item using useState, and defines two functions addItem and removeItem to modify the state. These functions are passed down to the AddItemForm component as props.
The AddItemForm component also uses useState to declare a state variable value, which represents the current value of the input field. When the form is submitted, the handleSubmit function is called, which adds the new item to the list by calling onAdd with the current value of value.
Because the items, addItem, and removeItem variables are declared inside the List component, they are not visible or modifiable from the outside. They are only accessible to the List component and its children, thanks to closure. This makes it easy to reuse the List component in other parts of your application, without worrying about name collisions or unintended side effects.
let’s see the implementation real quick
function useState(initialValue){
let value = initialValue
const state = value
const setState = (newValue) => value = newValue
return [state,setState]
}
const [message,setMessage] = useState('Hello')
console.log(message) // ??
setMessage('World')
console.log(message) // ??when we run this code the result will be
const [message,setMessage] = useState('Hello')
console.log(message) // Hello
setMessage('World')
console.log(message) // Hellowait what? what happened?
That wasn’t the correct implementation even if I said that was the correct one, sorry but
here is the catch, or if you spotted it good for you, you have understood what closure means.
let me show you where we got wrong when we implemented the useState or you can say bug
function useState(initialValue){
let value = initialValue
-- const state = value ❌
++ const state = () => value ✅ // making this as getter function
const setState = (newValue) => value = newValue // this is actual setter function
return [state,setState]
}
const [message,setMessage] = useState('Hello')
console.log(message()) // Hello, we calling message cuz it's a getter function now
setMessage('World')
console.log(message()) // WorldThe problem was the variable value is a private state/value. we are mutating the variable value using setMessage but we want to access the newly passed value through setMessage so we have to use closure to access the internal state/value, in this case, the variable value using variable state in the useState function.
In conclusion, Closures are a fundamental concept in JavaScript that can be used in react in many different ways. A private variable is one example of closure.