מה באמת הבעיה בדוגמת ריאקט הזו
אני אוהב לדמיין מה היה קורה אם. השבוע מתן בורנקראוט הציע תרגיל מחשבה כזה בפוסט כאן:
https://matanbobi.dev/posts/stop-passing-setter-functions-to-components
הקוד שהוא הדביק נראה ככה:
// 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>
);
};
הקומפוננטה Input מקבלת את setFormData
מ Form
ומפעילה אותה כשהטקסט משתנה. עכשיו לפני שנגיע ל"מה אם" אנחנו כבר רואים שלקומפוננטה Input יש בעיה - הפונקציה שהיא קיבלה מגיעה עם חתימה שלא מתאימה לה. קומפוננטת Input רוצה לדווח שהיה שינוי בשדה שלה, אבל צריכה לדווח את זה בצורה לא טבעית:
setFormData((prevData) => ({ ...prevData, name: event.target.value }));
הטענה של מתן בפוסט היתה שארכיטקטורה זו היא בעייתית כי אם מחר יהיה שינוי באיך ש Form שומר את הסטייט שלו, אז החתימה של setFormData
עשויה להשתנות ואז נצטרך לשנות גם את Input. המסקנה היא שלא כדאי להעביר setters שמתקבלים מ useState
לקומפוננטות פנימיות.
אומנם אהבתי את הדוגמה אבל אני לא בטוח שאני מסכים עם המסקנה. נדמיין שהטופס היה בנוי כך:
// Form.jsx
function Form() {
const [name, setName] = useState('');
const [favoriteColor, setFavoriteColor] = useState('#000000);
return (
<div>
<h1>Form</h1>
<Input label="Name" value={name} setter={setName} />
<Input label="Favorite Color" value={favoriteColor} setter={setFavoriteColor} />
</div>
);
};
// Input.jsx
function Input({ label, value, setter }) {
const handleInputChange = (event) => {
setter(event.target.value);
};
return (
<div>
<label>
{label}:
<input type="text" value={value} onChange={handleInputChange} />
</label>
</div>
);
};
במצב כזה עדיין העברתי את ה setters פנימה לקומפוננטת ה Input אבל אם יהיה שינוי באיך ש Form שומר את המידע לא תהיה בעיה לשנות את הקוד רק בקומפוננטת Form - למשל ליצור פונקציה חדשה שמקבלת את הערך החדש ולהעביר אותה פנימה ל Input. כלומר מה שהיה חשוב בדינמיקה בין שתי הקומפוננטות הוא הממשק ביניהן.
פה אפשר להוסיף התלבטות שניה והיא האם עדיף לשמור את כל אוביקט הטופס בתור State יחיד או לשבור אותו למספר משתנים פשוטים. בדוגמה אנחנו רואים ששימוש במספר משתנים פשוטים מעודד אותנו להעביר פחות מידע לקומפוננטות ולכן מבחינת ממשק אני ממליץ על שיטה זו.