useRef — είναι ένα hook του React που δημιουργεί ένα μεταβαλλόμενο αντικείμενο ref με ιδιότητα .current, το οποίο διατηρείται μεταξύ των render του component. Σε αντίθεση με το useState, η αλλαγή του .current δεν προκαλεί επαναφορά render, καθιστώντας το useRef ιδανικό για αποθήκευση αναφορών DOM, χρονοδιακοπτών και οποιωνδήποτε τιμών που πρέπει να επιβιώνουν στα render χωρίς να επανασχεδιάζουν το UI. Σύμφωνα με το React Documentation (2025), το αντικείμενο ref δημιουργείται μία φορά κατά τη διάρκεια ζωής του component και δεν αλλάζει μεταξύ των render, εγγυώμενο τη σταθερότητα της αναφοράς.
Κύρια σημεία
useRef — είναι ένα hook που προστέθηκε στο React 16.8 και επιστρέφει ένα μεταβαλλόμενο αντικείμενο ref με μία μόνο ιδιότητα .current. Η αρχική τιμή εκχωρείται στο .current κατά την τοποθέτηση (mount) του component. Η κύρια διαφορά από μια συνηθισμένη μεταβλητή — το αντικείμενο ref επιβιώνει της επαναφοράς render: κατά την επαναφορά render, το React δεν δημιουργεί ξανά το ref, αλλά επιστρέφει το ίδιο αντικείμενο.
Η εσωτερική υλοποίηση του useRef χρησιμοποιεί τον ίδιο μηχανισμό με τα useState και useEffect — μια συνδεδεμένη λίστα hooks στον κόμβο fiber. Ωστόσο, σε αντίθεση με το useState, το React δεν παρακολουθεί τις αλλαγές του αντικειμένου ref, δεν βάζει σε ουρά ενημερώσεις και δεν προγραμματίζει επαναφορά render. Αυτό καθιστά το useRef ένα εξαιρετικά ελαφρύ hook που δεν επηρεάζει την απόδοση ακόμα και με συχνές αλλαγές.
Σύμφωνα με το React Team — Hooks FAQ (2025), το useRef είναι ουσιαστικά ένα “kουτί” που αποθηκεύει μια τιμή. Συντακτική ζάχαρη: const ref = useRef(initialValue) είναι ισοδύναμο με const ref = { current: initialValue } με τη διαφορά ότι το React εγγυάται ότι το αντικείμενο ref θα είναι το ίδιο σε κάθε render. Χωρίς μαγεία — απλά μια σταθερή αναφορά που διαχειρίζεται το React.
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 αρχικά
// countRef.current = 5 μετά από μεταβολή
// Δεν συμβαίνει επαναφορά render!
}
Το πιο συνηθισμένο σενάριο χρήσης του useRef — η απόκτηση άμεσης πρόσβασης στο στοιχείο DOM. Το React εκχωρεί στο elementRef.current την αναφορά στον κόμβο DOM μετά την τοποθέτηση (mount) του component. Αυτό είναι απαραίτητο για την εστίαση input, τη μέτρηση διαστάσεων στοιχείου, την ενσωμάτωση με βιβλιοθήκες κινουμένων σχεδίων και βιβλιοθήκες που δεν χρησιμοποιούν render του React.
Το React διαχειρίζεται αυτόματα τις αναφορές ref στο DOM: κατά την τοποθέτηση εκχωρεί τον πραγματικό κόμβο DOM, κατά την αφαίρεση τον μηδενίζει (θέτει null). Αυτό εγγυάται ότι το .current περιέχει πάντα μια ενημερωμένη τιμή και αποτρέπει διαρροές μνήμης μέσω “kρεμαστών” αναφορών σε διαγραμμένα στοιχεία. Δεν απαιτείται χειροκίνητος καθαρισμός.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Εστίαση input μετά την τοποθέτηση του component
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
Σύμφωνα με το React — Refs and DOM documentation (2025), μην χρησιμοποιείτε το ref για δηλωτικές λειτουργίες — άνοιγμα/κλείσιμο modals, διαχείριση ορατότητας. Για αυτούς τους σκοπούς υπάρχουν το state και τα props. Τα refs προορίζονται για προστακτικές λειτουργίες που δεν μπορούν να εκφραστούν δηλωτικά: εστίαση, επιλογή κειμένου, ενσωμάτωση με βιβλιοθήκες τρίτων.
Το δεύτερο πιο σημαντικό σενάριο — αποθήκευση αυθαίρετων τιμών που πρέπει να διατηρούνται μεταξύ των render, αλλά η αλλαγή τους δεν πρέπει να προκαλεί επαναφορά render. Τυπικά παραδείγματα: αναγνωριστικά χρονοδιακοπτών (setInterval/setTimeout), σημαίες για ακύρωση αιτημάτων, προηγούμενες τιμές props για σύγκριση και οποιαδήποτε δεδομένα δεν επηρεάζουν την έξοδο του UI.
Αυτό είναι ιδιαίτερα χρήσιμο στο useEffect: το useRef αποθηκεύει το αναγνωριστικό του χρονοδιακόπτη και το cleanup το διαγράφει. Εάν το timerId αποθηκευτεί στο useState, κάθε κλήση setTimerId θα προκαλούσε μια περιττή επαναφορά render που δεν είναι απαραίτητη ούτε για τη λογική ούτε για το UI. Το useRef λύνει αυτό το πρόβλημα χωρίς επιβάρυνση και χωρίς περιττούς επανασχεδιασμούς.
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log('χτύπος');
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => stop, []);
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
Το κύριο δίλημμα — η επιλογή μεταξύ useRef και useState — λύνεται με μία ερώτηση: “Χρειάζεται επαναφορά render κατά την αλλαγή τιμής;”. Αν ναι — useState. Αν όχι — useRef. Το useState αποθηκεύει κατάσταση που επηρεάζει την έξοδο του component· το useRef αποθηκεύει δεδομένα που χρειάζονται για την εσωτερική λογική, αλλά δεν επηρεάζουν το UI.
Στην πράξη, οι προγραμματιστές συχνά χρησιμοποιούν το useRef για να αποθηκεύουν callbacks, για να αποφεύγουν προβλήματα με closures. Για παράδειγμα, εάν το useEffect εγγράφεται σε ένα συμβάν και μέσα στο callback χρειάζεται το τρέχον state — αποθηκεύστε το callback στο useRef. Σε κάθε render ενημερώστε το ref.current με τη νέα συνάρτηση και το εφέ θα καλεί πάντα το φρέσκο callback χωρίς επανεγγραφή.
| Κριτήριο | useRef | useState |
|---|---|---|
| Επαναφορά render | Δεν προκαλεί σε αλλαγή | Προκαλεί σε κάθε setState |
| Μεταβολή | Άμεση: ref.current = value | Μέσω setter: setState(value) |
| Χρήση σε JSX | Δεν χρησιμοποιείται (χωρίς εφέ) | Χρησιμοποιείται στην έξοδο του component |
| Παράδειγμα | Χρονοδιακόπτες, αναφορές DOM, προηγούμενες τιμές | Δεδομένα φόρμας, κατάσταση UI, σημαίες |
Υπάρχει ένα anti-pattern: η χρήση του useRef για αποθήκευση δεδομένων που χρειάζονται στο JSX, αλλά η αλλαγή τους δεν πρέπει να προκαλεί επαναφορά render. Αυτό οδηγεί σε αποσυγχρονισμό — το UI εμφανίζει παλιά δεδομένα, ενώ το ref.current είναι ήδη νέο. Εάν η τιμή εμφανίζεται στο UI — χρησιμοποιήστε useState. Εάν χρησιμοποιείται μόνο εντός της λογικής — useRef.
Ο συνδυασμός useRef + useEffect — ένα τυπικό μοτίβο για την παρακολούθηση προηγούμενων τιμών props. Αποθηκεύστε την προηγούμενη τιμή στο ref, συγκρίνετε με την τρέχουσα στο useEffect και λάβετε απόφαση βάσει της διαφοράς. Αυτό είναι ιδιαίτερα χρήσιμο σε κινούμενα σχέδια, όταν πρέπει να γνωρίζετε ποια τιμή ήταν πριν από την αλλαγή.
function PriceDisplay({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
const prevPrice = prevPriceRef.current;
if (price > prevPrice) {
animateUp();
} else if (price < prevPrice) {
animateDown();
}
prevPriceRef.current = price;
}, [price]);
return <span>${price}</span>;
}
Σύμφωνα με το React Documentation — Hooks FAQ (2025), αυτό το μοτίβο ονομάζεται “previous value pattern”. Λειτουργεί επειδή το useRef διατηρεί την τιμή μεταξύ των render και το useEffect εκτελείται μετά την επιβεβαίωση των αλλαγών. Πρώτα το DOM ενημερώνεται με τη νέα price, στη συνέχεια το useEffect συγκρίνει με την προηγούμενη (που είναι ακόμα στο ref) και ενημερώνει το ref.current στην τρέχουσα τιμή.
Το πιο συνηθισμένο λάθος — η ανάγνωση του ref.current στη φάση render για τον υπολογισμό JSX. Δεδομένου ότι η αλλαγή του ref.current δεν προκαλεί επαναφορά render, το component μπορεί να χρησιμοποιεί μια παλιά τιμή. Εάν η τιμή του .current συμμετέχει στην έξοδο UI — χρησιμοποιήστε useState. Εάν χρειάζεται να συγχρονίσετε ref και state, χρησιμοποιήστε useEffect για ενημέρωση του state από το ref.
// ❌ Μην διαβάζετε το ref.current κατά τη διάρκεια του render για εμφάνιση
function BadComponent() {
const valRef = useRef(0);
return <p>{valRef.current}</p>; // ΔΕΝ θα ενημερωθεί σε μεταβολή
}
// ✅ Χρησιμοποιήστε state για εμφάνιση, ref για λογική
function GoodComponent() {
const [val, setVal] = useState(0);
const valRef = useRef(0);
return <p>{val}</p>;
}
Ένα άλλο συνηθισμένο λάθος — η χρήση του useRef ως ο μοναδικός τρόπος αποθήκευσης κατάστασης στο component, όταν τα δεδομένα επηρεάζουν τελικά το UI. Οι προγραμματιστές επιλέγουν το useRef για να αποφύγουν “περιττές” επαναφορές render, αλλά στο τέλος λαμβάνουν ένα UI που δεν ενημερώνεται. Η σωστή προσέγγιση: χρησιμοποιήστε useState για δεδομένα UI και useRef μόνο για βοηθητικές τιμές που δεν συμμετέχουν στο render.
Συχνές ερωτήσεις
Ναι, αυτό είναι ένα κοινό μοτίβο — previous state pattern. Δημιουργήστε ένα ref και ενημερώστε το στο useEffect σε κάθε αλλαγή της παρακολουθούμενης τιμής. Τη στιγμή μεταξύ render και εφέ, το ref.current περιέχει την προηγούμενη τιμή που μπορεί να συγκριθεί με την τρέχουσα. Αυτό δεν απαιτεί πρόσθετες επαναφορές render και λειτουργεί με όλους τους τύπους δεδομένων.
Το React εκχωρεί στο ref.current την τιμή του κόμβου DOM μόνο αφού το στοιχείο έχει γίνει render και προστεθεί στο πραγματικό DOM. Κατά το πρώτο render, το component δεν έχει ακόμα τοποθετηθεί (mounted), επομένως το ref.current είναι ίσο με την αρχική τιμή (null). Η πρόσβαση στο DOM μέσω ref.current είναι δυνατή μόνο στο useEffect ή σε χειριστές γεγονότων που καλούνται μετά την τοποθέτηση.
createRef δημιουργεί ένα νέο αντικείμενο ref σε κάθε render — πρέπει να χρησιμοποιείται μόνο σε class components. useRef δημιουργεί το ref μία φορά και επιστρέφει το ίδιο αντικείμενο σε όλα τα επόμενα render. Σε functional components, χρησιμοποιείτε αποκλειστικά το useRef· το createRef θα οδηγήσει σε απώλεια της τιμής κατά την επαναφορά render, καθώς το αντικείμενο θα δημιουργηθεί ξανά.
Ναι, το αντικείμενο ref μπορεί να μεταβιβαστεί ως κανονικό prop: <Child inputRef={inputRef} />. Το child component το χρησιμοποιεί μέσω inputRef.current. Ωστόσο, για την απευθείας μεταβίβαση ref στο στοιχείο DOM του child component, χρησιμοποιήστε το forwardRef — ένα HOC που επιτρέπει τη διέλευση του ref μέσω props. Χωρίς forwardRef, το ref δεν θα συνδεθεί αυτόματα με το στοιχείο DOM μέσα στο child component.
Συνδέστε το ref στο στοιχείο, στη συνέχεια στο useEffect διαβάστε το ref.current.getBoundingClientRect() ή το ref.current.offsetWidth / offsetHeight. Για αντιδραστική παρακολούθηση αλλαγών μεγέθους, χρησιμοποιήστε ResizeObserver μέσα στο useEffect: δημιουργήστε observer, εγγραφείτε σε αλλαγές και ενημερώστε το state με νέες διαστάσεις. Μην ξεχάσετε να απενεργοποιήσετε τον observer στο cleanup.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης