זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/render-usecallback
זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/render-usecallback
האם במקרה הזה לא יותר פשוט להוסיף ל-memo ב-ColorBox את הפונקציה isEqual שתגיד לריאקט לעשות רינדור מחדש רק כאשר הצבע משתנה?
כך:
import React from 'react';
import { useState } from 'react';
import tinycolor from 'tinycolor2';
const ColorBox = React.memo(function ColorBox(props) {
console.log('Color Box');
const { start, spin, onClick, id } = props;
const color = tinycolor(start).spin(spin).toString();
return (
<div
onClick={onClick}
data-id={id}
style={{
width: '100px',
height: '100px',
background: color,
display: 'inline-block',
margin: '5px',
}} >{id}</div>
);
},(prevProps , nexpProps)=>{
return prevProps.start === nexpProps.start;
});
export default React.memo(function ColorPalette(props) {
console.log('Color Palette');
const { start } = props;
const [deletedBoxes, setDeletedBoxes] = useState(new Set());
function removeBox(e) {
const id = e.target.dataset.id;
deletedBoxes.add(Number(id));
setDeletedBoxes(new Set(deletedBoxes));
}
const colors = [];
for (let i=-360; i < 360; i++) {
if (deletedBoxes.has(i)) continue;
colors.push(
<ColorBox
key={i}
start={start}
spin={i}
onClick={removeBox}
id={i}
/>
);
}
return colors;
});
אז זאת באמת הצעה מעניינת ועובדת אבל יש פה נקודה עדינה שכדאי לשים לב אליה: כל פעם שלוחצים על קופסה הקוד שכתבת ישתמש באותה פונקציית removeBox שמחוברת לאותו משתנה deletecBoxes ששמור בסטייט. הקוד שלך בתוך הפונקציה מעדכן את משתנה הסטייט, אבל בפעמים הבאות שמפעילים את הפונקציה היא עובדת על המשתנה deletedBoxes הישן שהיה שמור בסטייט ולא רואה את העדכון.
קשה לראות את זה כמו שהקוד כתוב בגלל ש Set ב JavaScript הוא Mutable Object - זה אומר שכל פעם ש removeBox שלך נקראת היא לוקחת את הערך של deletedBox הישן, מוסיפה לו תיבה חדשה ומעדכנת את משתנה הסטייט מחדש לערך הזה.
אבל אם אני מעדכן קצת את הקוד אפשר לראות שתוספת פונקציונאליות לכאורה לא קשורה כבר שוברת אותו - תראה כאן:
הוספתי כפתור “Delete Random Box” שמוחק קופסה אקראית. נסה ללחוץ על קופסה כדי למחוק אותה, אחר כך ללחוץ על הכפתור (ותראה איזה קופסה הוא מחק - יש גם alert) ואחר כך למחוק קופסה נוספת באמצעות לחיצה עליה. מה שיקרה שבלחיצה על הקופסה השניה הקופסה האקראית שמחקת חוזרת למסך.
הסיבה היא ששני המנגנונים (מחיקה באמצעות לחיצה על קופסה ומחיקה באמצעות הכפתור “מחק בצורה אקראית”) לא מסונכרנים. כל אחד מהם מסתכל על משתנה deletedBoxes אחר. הסיבה ליציאה מסינכרון היא הקומפוננטה הפנימית שלך ColorBox שממשיכה להשתמש בפונקציית removeBox הישנה גם אחרי שהיה עדכון ל ColorBox, בגלל אותו Memo
אני מקווה שההסבר היה ברור התופעה באמת קצת עדינה אבל חשוב לשים לב גם לדברים עדינים כשכותבים ריאקט. נסה לשחק עם הקוד שקישרתי ב codesandbox כדי לראות את זה.
הבנתי.
תודה רבה על ההסבר המפורט!
בדוגמא שהבאת רואים את הבעיה ממש ברור.