useRef הוא React Hook המאפשר לך להתייחס לערך שאינו נחוץ לעיבוד.

const ref = useRef(initialValue)

הפניה

useRef(initialValue)

התקשר ל-useRef ברמה העליונה של הרכיב שלך כדי להכריז על ref.

import { useRef } from 'react';

function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...

ראה דוגמאות נוספות למטה.

פרמטרים

  • initialValue: הערך שאתה רוצה שמאפיין current של אובייקט ref יהיה בהתחלה. זה יכול להיות ערך מכל סוג שהוא. טענה זו מתעלמת לאחר העיבוד הראשוני.

מחזירה

useRef מחזיר אובייקט עם מאפיין יחיד:

  • current: בתחילה, הוא מוגדר ל-initialValue שעברת. מאוחר יותר תוכל להגדיר אותו למשהו אחר. אם תעביר את אובייקט ref ל-React כתכונה ref לצומת JSX, React יגדיר את המאפיין current שלו.

בעיבודים הבאים, useRef יחזיר את אותו אובייקט.

אזהרות

  • אתה יכול לשנות את המאפיין ref.current. שלא כמו state, זה ניתן לשינוי. עם זאת, אם הוא מכיל אובייקט שהוא used לעיבוד (לדוגמה, חלק מה-state שלך), אז אל תעשה מוטציה לאובייקט זה.
  • כאשר אתה משנה את המאפיין ref.current, React אינו מעבד מחדש את הרכיב שלך. React לא מודע מתי אתה משנה את זה מכיוון שuse רפר הוא אובייקט JavaScript רגיל.
  • אל תכתוב או קרא ref.current במהלך העיבוד, למעט אתחול. זה הופך את התנהגות הרכיב שלך לבלתי צפויה.
  • במצב קפדני, React תתקשר לפונקציית הרכיב שלך פעמיים ​​כדי לעזור לך למצוא זיהומים מקריים. זוהי התנהגות לפיתוח בלבד ואינה משפיעה על הייצור. כל אובייקט ref ייווצר פעמיים, אך אחת מהגרסאות תימחק. אם פונקציית הרכיב שלך טהורה (כפי שהיא צריכה להיות), זה לא אמור להשפיע על ההתנהגות.

שימוש

הפניית ערך עם ref

התקשר ל-useRef ברמה העליונה של הרכיב שלך כדי להכריז על אחד או יותר refs.

import { useRef } from 'react';

function Stopwatch() {
const intervalRef = useRef(0);
// ...

useRef מחזירה ref object עם current מאפיין בודד שהוגדר תחילה לערך ההתחלתי שסיפקת.

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

החלפת רפ’ לא מפעילה עיבוד מחדש. פירוש הדבר שהשו”פים מושלמים לאחסון מידע שאינו משפיע על הפלט החזותי של הרכיב שלך. לדוגמה, אם אתה צריך לאחסן מזהה מרווח ולאחזר אותו מאוחר יותר, אתה יכול לשים אותו ב-ref. כדי לעדכן את הערך בתוך ה-ref, עליך לשנות באופן ידני את current המאפיין שלו:

function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}

מאוחר יותר, תוכל לקרוא את מזהה המרווח הזה מהשופט כדי שתוכל להתקשר לנקות את המרווח הזה:

function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}

על ידי שימוש ב-Ref, אתה מבטיח כי:

  • ניתן לאחסן מידע בין רינדור מחדש (בניגוד למשתנים רגילים, שמתאפסים בכל עיבוד).
  • שינוי זה לא מפעיל רינדור מחדש (בניגוד למשתנים state, שמפעילים עיבוד מחדש).
  • המידע הוא מקומי לכל עותק של הרכיב שלך (בניגוד למשתנים שבחוץ, המשותפים).

שינוי רפ אינו מפעיל רינדור מחדש, כך ששו”פים אינם מתאימים לאחסון מידע שברצונך להציג על המסך. השתמש ב-state עבור זה במקום זאת. קרא עוד על בחירה בין useRef ל-useState.

Examples of referencing a value with useRef

Example 1 of 2:
מונה קליקים

רכיב זה use הוא רשופט כדי לעקוב אחר מספר הפעמים שנלחץ על הכפתור. שימו לב שזה בסדר לכתוב use ר”פ במקום state כאן כי use ספירת הקליקים נקראת ונכתבת רק במטפל באירועים.

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  }

  return (
    <button onClick={handleClick}>
      Click me!
    </button>
  );
}

אם תראה {ref.current} ב-JSX, המספר לא יתעדכן בלחיצה. זוהי מכיוון שהגדרת ref.current לא מפעילה עיבוד מחדש. מידע שהוא used לעיבוד צריך להיות state במקום זאת.

Pitfall

אל תכתוב או קרא ref.current במהלך העיבוד.

React מצפה שהגוף של הרכיב שלך יתנהג כמו פונקציה טהורה:

  • אם הקלט (props, state, ו-context צריכים להחזיר בדיוק אותו הקשר,__))
  • קריאה לזה בסדר אחר או בטיעונים שונים לא אמורה להשפיע על תוצאות שיחות אחרות.

קריאה או כתיבה של שופט במהלך עיבוד שוברים את הציפיות הללו.

function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return <h1>{myOtherRef.current}</h1>;
}

במקום זאת, אתה יכול לקרוא או לכתוב שופטים ממטפלי אירועים או אפקטים.

function MyComponent() {
// ...
useEffect(() => {
// ✅ You can read or write refs in effects
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ You can read or write refs in event handlers
doSomething(myOtherRef.current);
}
// ...
}

אם אתה חייב לקרוא או לכתוב משהו במהלך העיבוד, use state במקום זאת.

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


מניפולציה של DOM עם שו”ת

זה נפוץ במיוחד ל-use שופט כדי לתפעל את DOM. React יש תמיכה מובנית לכך.

ראשית, הכריז על ref object עם ערך התחלתי של null:

import { useRef } from 'react';

function MyComponent() {
const inputRef = useRef(null);
// ...

לאחר מכן העבר את אובייקט ה-ref שלך כתכונה ref ל-JSX של הצומת DOM שברצונך לתפעל:

// ...
return <input ref={inputRef} />;

לאחר React יוצר את הצומת DOM ומעלה אותו על המסך, React יגדיר את current המאפיין של אובייקט ה-ref שלך לצומת DOM זה. עכשיו אתה יכול לגשת לצומת DOM של <input> ולשיטות קריאה כמו focus():

function handleClick() {
inputRef.current.focus();
}

React יחזיר את המאפיין current ל-null כאשר הצומת יוסר מהמסך.

קרא עוד על מניפולציה של DOM עם refs.

Examples of manipulating the DOM with useRef

Example 1 of 4:
מיקוד קלט טקסט

בדוגמה זו, לחיצה על הכפתור תתמקד בקלט:

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}


הימנעות מיצירה מחדש של תוכן השופט

React שומר את ערך ה-ref הראשוני פעם אחת ומתעלם ממנו בעיבודים הבאים.

function Video() {
const playerRef = useRef(new VideoPlayer());
// ...

למרות שהתוצאה של new VideoPlayer() היא רק used עבור העיבוד הראשוני, אתה עדיין קורא לפונקציה הזו בכל עיבוד. זה יכול להיות בזבזני אם זה יוצר חפצים יקרים.

כדי לפתור את זה, אתה יכול לאתחל את השופט כך במקום זאת:

function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...

בדרך כלל, כתיבה או קריאה של ref.current במהלך העיבוד אינם מותרים. עם זאת, זה בסדר במקרה הזה כי התוצאה היא תמיד זהה, והתנאי מופעלת רק במהלך האתחול, כך שהוא ניתן לחיזוי מלא.

Deep Dive

כיצד להימנע מבדיקות ריק בעת אתחול useRef מאוחר יותר

אם אתה use בודק סוגים ולא רוצה לבדוק תמיד את null, אתה יכול לנסות דפוס כזה במקום זאת:

function Video() {
const playerRef = useRef(null);

function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}

// ...

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


פתרון בעיות

אני לא יכול לקבל רפרנט לרכיב מותאם אישית

אם תנסה להעביר ref לרכיב משלך כך:

const inputRef = useRef(null);

return <MyInput ref={inputRef} />;

ייתכן שתקבל שגיאה בקונסולה:

Console
אזהרה: לא ניתן לתת רפים לרכיבי פונקציה. ניסיונות לגשת לשופט זה ייכשלו. האם התכוונת לuse React.forwardRef()?

כברירת מחדל, הרכיבים שלך אינם חושפים רפרנסים לצמתים DOM שבתוכם.

כדי לתקן זאת, מצא את הרכיב שאליו ברצונך לקבל רפרנט:

export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}

ואז עטפו אותו ב-forwardRef כך:

import { forwardRef } from 'react';

const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});

export default MyInput;

אז רכיב האב יכול לקבל ר”פ אליו.

קרא עוד על גישה לצמתי DOM של רכיב אחר.