• בלוג
  • [ריאקט] אפקט לחישוב אסינכרוני הוא Anti Pattern

[ריאקט] אפקט לחישוב אסינכרוני הוא Anti Pattern

15/12/2022

אם קומפוננטת ריאקט מקבלת ערך ב prop וצריכה להציג מידע מחושב מהערך הזה, כולנו יודעים לא לשמור את תוצאת החישוב ב State נפרד ופשוט לחשב אותו כל Render, לדוגמה:

function Twice({ x }) {
    return <p>{x} * 2 = {x * 2}</p>
}

אבל כשמדובר בחישוב אסינכרוני אנחנו נוטים להתבלבל - נזכרים שיש useEffect, חושבים שכדאי לחשב את הערך באיזה אפקט, לשמור אותו בסטייט נפרד ואז לרנדר מחדש כשיהיה מוכן. משהו כזה:

// Don't Do This:
function DataFromApi({ id }) {
    const [name, setName] = useState(null);

    useEffect(() => {
        fetch(`/api/product/${id}`)
            .then(p => p.json())
            .then(p => setName(p.name));
    }, [id]);

    if (!name) {
        return <p>Loading...</p>
    }

    return <p>Product Name = {name}</p>
}

אבל זאת Anti Pattern. אלה הבעיות המרכזיות עם הגישה:

  1. אם הקומפוננטה יוצאת מהמסך וחוזרת, הערך שלכם נמחק וצריך להביא אותו מחדש. הסטייט הוא לא מקום מספיק "יציב" כדי לשמור בו מידע שמגיע מהרשת (או מידע שהתאמצתם לחשב באופן כללי).

  2. יש לנו נטיה לטעות במערך ה Dependencies של useEffect לעתים הרבה יותר קרובות ממה שנדמה לכם, וטעויות כאלה מאוד קשות לזיהוי. סיכוי טוב שהחישוב שלכם ירוץ הרבה יותר פעמים ממה שאתם באמת צריכים.

כשאנחנו רוצים לחשב מידע בצורה אסינכרונית ולהשתמש בתוצאה בקומפוננטה, הרבה יותר מומלץ להפריד בין "אני רוצה לחשב את המידע הזה" לבין "אני צריך להוציא את המידע שחושב מהמחסן כדי להציג אותו". הקומפוננטה אחראית רק על החלק השני, אבל את התחלת החישוב ושמירת התוצאות כדאי לבצע מחוץ לעץ. רידאקס מספק פיתרון טוב באופן כללי, וכשמדובר בתקשורת ספריות כמו swr או react-query מציעות פיתרון טוב תוך שימוש ב Context.