・The following example allows a developer to pass the setter function directly to its child component.
// Form.jsx
function Form() {
const [formData, setFormData] = useState({ name: '' });
return (
<div>
<h1>Form</h1>
{/* Pass the setter function down to ChildComponent */}
<Input name={formData.name} setFormData={setFormData} />
<button onClick={() => console.log(formData)}>Submit</button>
</div>
);
};
// Input.jsx
function Input({ name, setFormData }) {
const handleInputChange = (event) => {
// Directly using the setFormData setter function from the parent
setFormData((prevData) => ({ ...prevData, name: event.target.value }));
};
return (
<div>
<label>
Name:
<input type="text" value={name} onChange={handleInputChange} />
</label>
</div>
);
};
Abstraction Leak
・An abstraction leak occurs when one component knows too much about another component's internal implementation. In this case, the Input component makes the following assumption:
The parent component uses the
useStatehook.The state contains a
namefield directly, alongside other data.The parent will always remain in the same state.
These assumptions create a tight coupling between the child and parent components, meaning any change to the parent's state structure or management mechanism requires an update to the child.
Why is that so bad?
-Fragility: Changes to the parent component's logic break the child component, creating a maintenance headache.
-Reduced Reusability: The child is tied to a specific parent implementation, which limits its use in other contexts.
-loss of Clarity: Using raw setState makes it unclear which part of the child component is supposed to be modified.
How do we solve the leak?
・Solving the abstraction leak in this case is extremely simple. The Input component does not need to accept a prop with the actual setter function. Instead, it can receive a callback function that encapsulates the state change. For example, it could be a function named handleNameChange.
// Form.jsx
function Form() {
const [formData, setFormData] = useState({ name: '' });
const handleNameChange = (name) => {
setFormData((prevState) => ({...prevState, name}));
};
return (
<div>
<h1>Form</h1>
{/* Pass the setter function down to Input */}
<Input name={formData.name} onChange={handleNameChange} />
<button onClick={() => console.log(formData)}>Submit</button>
</div>
);
};
// Input.jsx
function Input({ name, onChange }) {
const handleInputChange = (event) => {
onChange(event.target.value);
};
return (
<div>
<label>
Name:
<input type="text" value={name} onChange={handleInputChange} />
</label>
</div>
);
};
Top comments (0)