רוצים להשתלב בתעשיית ההייטק?למסלולי הכשרה ב-Full Stack, סייבר ו-AI לחצו כאן
React logo

useEffect – הסבר מלא ודוגמאות

מה זה useEffect? (תקציר מהיר)

useEffect הוא Hook בריאקט שמאפשר לבצע השפעות צד (Side Effects) בתוך קומפוננטות פונקציונליות — בעצם כל עבודה שמחוץ לרינדור עצמו: מנויים (subscriptions), שליפת נתונים (fetch), עדכון ישיר של ה-DOM, מאזיני אירועים, טיימרים ועוד.

  • useEffect hook
  • מקבל שני ארגומנטים (השני אופציונלי)
  • הארגומנט הראשון – פונקציית callback
  • הארגומנט השני – מערך תלותים (dependency array)
  • ברירת מחדל: רץ אחרי כל רינדור (כולל הראשון)
  • ה-callback לא יכול להחזיר Promise (אל תסמנו אותו async)
  • אם מערך התלותים ריק [] – ירוץ פעם אחת רק ברינדור הראשוני (mount)

תוכן עניינים

  1. הקדמה
  2. UseEffectBasics – דוגמה בסיסית
  3. Multiple Effects – כמה אפקטים נפרדים
  4. Conditional – תנאי בלי לשבור Hooks
  5. Cleanup – מניעת דליפות/עדכונים
  6. טיפים ודגשים חשובים

1)מה זה:

useEffect מריץ קוד אחרי הרינדור. ניתן להחזיר פונקציה מתוך האפקט לצרכי ניקוי(Cleanup) שתופעל לפני הריצה הבאה של האפקט או בעת הסרת הקומפוננטה מהמסך (unmount).

useEffect(() => {
  // קוד שירוץ אחרי הרינדור
  return () => {
    // ניקוי: ירוץ לפני אפקט הבא או בעת unmount
  };
}, [/* תלותים */]);
  • בלי מערך תלות – רץ אחרי כל רינדור (כולל הראשון).
  • עם [] – רץ פעם אחת ב-mount; ה-cleanup ירוץ ב-unmount.
  • עם [a, b] – רץ ב-mount ובכל שינוי של אחד התלותים.

למה לפעמים נוצרת לולאה אינסופית?

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

❌ דוגמה בעייתית

const Bad = () => {
  const [value, setValue] = useState(0);
  const sayHello = () => {
    console.log('hello world');
    setValue(value + 1); // יגרום לרינדור נוסף מיד
  };
  sayHello(); // רץ בכל רינדור!
  return <h1>value: {value}</h1>;
};

✅ התיקון (העברה ל-useEffect חד-פעמי)

const Good = () => {
  const [value, setValue] = useState(0);
  useEffect(() => {
    console.log('hello world');
    setValue(v => v + 1); // ירוץ פעם אחת בלבד
  }, []);
  return (
    <div>
      <h1>value: {value}</h1>
      <button className="btn" onClick={() => setValue(v => v + 1)}>click me</button>
    </div>
  );
};

2) UseEffectBasics – דוגמה בסיסית

בדוגמה הבאה נראה איך קוד שנמצא מחוץ ל-useEffect רץ בכל רינדור, לעומת קוד שנמצא בתוךuseEffect עם [] שרץ פעם אחת בלבד (Mount).

import { useState, useEffect } from 'react';

const UseEffectBasics = () => {
  const [value, setValue] = useState(0);
  const sayHello = () => {
    console.log('hello there');
  };

  sayHello();

  // useEffect(() => {
  //   console.log('hello from useEffect');
  // });

  useEffect(() => {
    console.log('hello from useEffect');
  }, []);
  return (
    <div>
      <h1>value : {value}</h1>
      <button className='btn' onClick={() => setValue(value + 1)}>
        click me
      </button>
    </div>
  );
};
export default UseEffectBasics;
דוגמה חיה
value: 0

3) Multiple Effects – כמה אפקטים נפרדים

ניתן להגדיר כמה useEffect באותו רכיב, לכל אחד תלותים משלו ולוגיקה משלו. זה מסייע להפרדת אחריות (Separation of Concerns).

// import Starter from './tutorial/02-useEffect/starter/03-multiple-effects.jsx';

import { useState, useEffect } from 'react';

const MultipleEffects = () => {
  const [value, setValue] = useState(0);
  const [secondValue, setSecondValue] = useState(0);

  useEffect(() => {
    console.log('hello from first useEffect');
  }, [value]);

  useEffect(() => {
    console.log('hello from second useEffect');
  }, [secondValue]);
  return (
    <div>
      <h1>value : {value}</h1>
      <button className='btn' onClick={() => setValue(value + 1)}>
        value
      </button>
      <h1>second value : {secondValue}</h1>
      <button className='btn' onClick={() => setSecondValue(secondValue + 1)}>
        second value
      </button>
    </div>
  );
};
export default MultipleEffects;
דוגמה חיה
value: 0
second value: 0

4) React.useEffect – Conditional

אסור לעטוף useEffect עצמו בתוך if/for/while/try — זה שובר את סדר ה-Hooks. במקום זאת, שמים תנאי בתוך האפקט או שולטים בהרצה דרך מערך התלותים.

✅ תנאי בתוך האפקט

useEffect(() => {
  if (!shouldRun) return; // יציאה מוקדמת
  // לוגיקה שרצה רק כש-shouldRun=true
}, [shouldRun]);

✅ ריצה רק כאשר count זוגי

useEffect(() => {
  if (count % 2 !== 0) return;
  console.log('even:', count);
}, [count]);

✅ דילוג על הריצה הראשונה (skip first run)

const first = useRef(true);
useEffect(() => {
  if (first.current) { first.current = false; return; }
  // ירוץ רק מהרינדור השני והלאה
}, [value]);
עדכון כותרת טאב (title)
0
אפקט שרץ רק בערכים זוגיים
0 (רץ רק כשזוגי)

5) ניקוי (Cleanup) – למנוע עדכונים/דליפות

ה-cleanup מחזיר את המערכת למצב נקי בין ריצות האפקט/בעת unmount: מסירים מאזינים, מבטלים טיימרים, סוגרים מנויים ומבטלים בקשות רשת פעילות כדי למנוע setState אחרי שהרכיב הוסר.

🟢 מאזין אירועים

useEffect(() => {
  const onResize = () => console.log('resizing...');
  window.addEventListener('resize', onResize);
  return () => window.removeEventListener('resize', onResize);
}, []);

🟢 טיימר / אינטרוול

useEffect(() => {
  const id = setInterval(() => console.log('tick'), 1000);
  return () => clearInterval(id);
}, []);

🟢 בקשת API עם ביטול (AbortController)

useEffect(() => {
  const ac = new AbortController();
  (async () => {
    try {
      const res = await fetch(url, { signal: ac.signal });
      const json = await res.json();
      setData(json);
    } catch (err) {
      if (err.name !== 'AbortError') console.error(err);
    }
  })();
  return () => ac.abort();
}, [url]);

🟢 דיבאונס (Debounce) לשדה קלט

const [input, setInput] = useState('');
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
  const id = setTimeout(() => setSearchTerm(input), 300);
  return () => clearTimeout(id);
}, [input]);
דוגמה חיה: דיבאונס
searchTerm:
דוגמה חיה: מאזין Resize עם ניקוי
window.innerWidth = 0px

6) טיפים ודגשים

  • אל תכתבו async ישירות על פונקציית useEffect; כתבו פונקציה פנימית אסינכרונית והפעילו אותה בתוך האפקט.
  • העדיפו עדכון פונקציונלי: setValue(v => v + 1) – מפחית תלותים ושומר על ערך עדכני בתורים.
  • כל ערך/פונקציה שנעשה בו שימוש פנימי צריך להופיע במערך התלותים (או להיות ממומואז ב-useCallback/useMemo).
  • במצב פיתוח עם Strict Mode ייתכן שתרוץ סימולציית mount/unmount נוספת כדי לחשוף בעיות; בפרודקשן זה לא קורה.
  • ניתן לפרק לוגיקה למספר useEffect נפרדים במקום לאחד הכול לאפקט אחד גדול – זה קריא ובטוח יותר.

🚀 הלאה

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

8) useEffect + Fetch Data — למה ואיך?

למה useEffect? כי שליפת נתונים מהאינטרנט (fetch) היא "השפעת צד" — אסור להריץ אותה בתוך פונקציית הרינדור. useEffect רץ אחרי הרינדור, מאפשר לנו לשלוט בתזמון (פעם אחת עם []) ולהחזיר פונקציית ניקוי.

  • [] גורם לאפקט לרוץ פעם אחת במונט (עלייה ראשונית של הקומפוננטה).
  • AbortController מבטל את הבקשה אם הקומפוננטה מתפרקת — אין דליפות.
  • setUsers(data) מעדכן state וגורם ל־re-render שמציג את הרשימה.
  • ב־Strict Mode (פיתוח) האפקט יכול להתרוץ פעמיים — הניקוי מגן עלינו.
// מסמן לקומפוננטה שהיא קומפוננטת-לקוח (Client Component) בסביבת Next.js (App Router)
"use client";

// מייבאים שני Hooks מ-React: useState (ניהול מצב) ו-useEffect (הרצת אפקטים אחרי רינדור)
import { useState, useEffect } from "react";

// כתובת ה-API שממנה נשלוף את רשימת משתמשי GitHub (מערך של אובייקטים)
const URL = "https://api.github.com/users";

// הגדרת קומפוננטת React פונקציונלית בשם FetchData וייצוא כברירת מחדל
export default function FetchData() {
  // users: מצב שמחזיק את נתוני המשתמשים (ברירת מחדל: מערך ריק)
  // setUsers: פונקציה לעדכון users
  const [users, setUsers] = useState([]);     // default []

  // loading: דגל טעינה — true עד שהבאת הנתונים מסתיימת
  // setLoading: עדכון דגל הטעינה
  const [loading, setLoading] = useState(true);

  // error: מחרוזת שגיאה לתצוגה אם משהו נכשל
  // setError: עדכון הודעת השגיאה
  const [error, setError] = useState("");

  // useEffect ירוץ אחרי הרינדור הראשון בלבד (בזכות []), כדי לבצע את ה-fetch מה-API
  useEffect(() => {
    // AbortController מאפשר לבטל את בקשת ה-fetch אם הקומפוננטה מתפרקת
    const ctrl = new AbortController();

    // פונקציה אסינכרונית שמבצעת את שליפת הנתונים
    const fetchData = async () => {
      try {
        // fetch לכתובת ה-API; מעבירים signal כדי שנוכל לבטל אם צריך
        const res = await fetch(URL, { signal: ctrl.signal });

        // אם הסטטוס לא תקין (לא 200-299) — נזרוק שגיאה ידנית
        if (!res.ok) throw new Error(`HTTP ${res.status}`);

        // ממירים את ה-Response ל-JSON — צפוי לקבל מערך של משתמשים
        const data = await res.json();

        // שומרים את התוצאה ב-state; זה יגרום לרינדור מחדש עם הנתונים
        setUsers(data); // set users equal to result
      } catch (err) {
        // אם זו לא שגיאת ביטול (AbortError) — נשמור את הודעת השגיאה להצגה
        if (err.name !== "AbortError") setError(String(err.message || err));
      } finally {
        // בכל מקרה (הצלחה/כישלון) — נכבה את מסך הטעינה
        setLoading(false);
      }
    };

    // מפעילים את שליפת הנתונים פעם אחת ברינדור הראשוני
    fetchData();          // runs only on initial render

    // פונקציית ניקוי: אם הקומפוננטה מתנתקת — נבטל את הבקשה כדי למנוע דליפות
    return () => ctrl.abort();
  }, []); // מערך תלות ריק → מריץ את האפקט פעם אחת בלבד

  // תצוגה במהלך טעינה: כותרת + טקסט "loading…"
  if (loading) {
    return (
      <section>
        <h3>github users</h3>
        <p>loading…</p>
      </section>
    );
  }

  // תצוגת שגיאה במקרה של כשל: כותרת + הודעת השגיאה באדום
  if (error) {
    return (
      <section>
        <h3>github users</h3>
        <p style={{ color: "tomato" }}>error: {error}</p>
      </section>
    );
  }

  // תצוגת ברירת המחדל — אחרי שהנתונים נטענו בהצלחה
  return (
    <section>
      {/* כותרת הסקשן */}
      <h3>github users</h3>

      {/* רשימת המשתמשים; listStyle:none כדי להסתיר בולטים; padding:0 לאיפוס */}
      <ul className="users" style={{ listStyle: "none", padding: 0 }}>
        {/* מעבר על המערך users והחזרת <li> לכל משתמש */}
        {users.map((user) => {
          // פירוק מאפייני משתמש: id (מפתח ייחודי), login (שם משתמש),
          // avatar_url (תמונת פרופיל), html_url (קישור לעמוד הפרופיל)
          const { id, login, avatar_url, html_url } = user;

          // אלמנט רשימה לכל משתמש
          return (
            <li
              key={id} // React משתמש במפתח ליעילות ברינדור רשימות
              style={{ display: "flex", gap: 12, margin: "12px 0" }} // פריסה אופקית וריווח
            >
              {/* תמונת אווטאר; ב-Next.js אפשר להחליף ל-next/image עם הגדרת דומיין */}
              <img
                src={avatar_url} // כתובת לתמונת הפרופיל
                alt={login}      // טקסט חלופי נגיש
                width={48}
                height={48}
                style={{ borderRadius: 8 }} // פינות מעוגלות
              />

              {/* פרטי המשתמש: שם + קישור לפרופיל */}
              <div>
                {/* שם המשתמש; margin:0 כדי לשמור על קומפקטיות */}
                <h5 style={{ margin: 0 }}>{login}</h5>

                {/* קישור לפרופיל GitHub; נפתח בטאב חדש; rel לשיקולי אבטחה */}
                <a href={html_url} target="_blank" rel="noreferrer">
                  profile
                </a>
              </div>
            </li>
          );
        })}
      </ul>
    </section>
  );
}

תוצאה

useEffect מריץ את ה־fetch פעם אחת במונט (בזכות []), שומר תוצאה ב־state ומרנדר מחדש. יש גם ניקוי עם AbortController.

דוגמה חיה

טוען…

📚 מקורות מומלצים (Docs)

העמודים הרשמיים והמסכמים שכדאי לקרוא לעומק:

למה ללמוד תכנות וסייבר ב-More-ways?

עולם הטכנולוגיה משתנה במהירות, והיום כבר לא מספיק רק לדעת לכתוב קוד. ב-More-ways אנחנו מאמינים בשילוב קריטי בין פיתוח Full-Stack, הבנת ארכיטקטורת רשתות, ושימוש מתקדם ב-בינה מלאכותית (AI). המדריך שלפניכם הוא רק טעימה מהידע המקצועי שאנו מעבירים בקורסים שלנו, הכוללים ליווי אישי, פרויקטים מעשיים והכנה לשוק העבודה המודרני.

האם אפשר ללמוד תכנות ללא ניסיון?

בוודאי. המסלולים שלנו ב-More-ways מונגשים גם למתחילים, עם דגש על חשיבה לוגית ובניית בסיס איתן לפני הצלילה לקוד המורכב.

איך AI משתלב בלימודי הייטק?

אנחנו מלמדים אתכם להשתמש בכלי AI כדי להאיץ את הפיתוח, לדבג קוד ביעילות ולבנות אפליקציות חכמות יותר בזמן קצר יותר.

מוכנים לקחת את הקריירה לשלב הבא?

מעבר לאתר More-ways הרשמי