useContext — είναι ένα hook του React που παρέχει στα functional components άμεση πρόσβαση σε δεδομένα από το context, που δημιουργήθηκε μέσω του createContext. Το context στο React λύνει το πρόβλημα του props drilling — της μεταβίβασης props μέσω πολλών ενδιάμεσων components που τα ίδια δεν χρησιμοποιούν αυτά τα δεδομένα. Σύμφωνα με το React Documentation (2025), το useContext λαμβάνει ένα αντικείμενο context και επιστρέφει την τρέχουσα τιμή που έχει οριστεί από τον πλησιέστερο Provider ψηλότερα στο δέντρο components. Όταν αλλάξει η τιμή στον Provider, όλα τα components που χρησιμοποιούν useContext ανασχεδιάζονται αυτόματα.
Βασικά σημεία
useContext — είναι ένα hook που προστέθηκε στο React 16.8 μαζί με τα υπόλοιπα hooks, το οποίο επιτρέπει την ανάγνωση μιας τιμής από το context του React. Το context είναι ένας μηχανισμός ενσωματωμένος στο React, σχεδιασμένος για τη μεταβίβαση δεδομένων μέσα στο δέντρο components χωρίς την ανάγκη χειροκίνητης μεταβίβασης props σε κάθε επίπεδο. Το useContext αντικαθιστά το component Consumer από το παλιό Context API και κάνει τον κώδικα πιο συνοπτικό και ευανάγνωστο.
Τυπικά σενάρια χρήσης του context περιλαμβάνουν θέματα εμφάνισης (ανοιχτό/σκοτεινό), τοπικές ρυθμίσεις και μεταφράσεις (i18n), έλεγχο ταυτότητας χρήστη, ρυθμίσεις εφαρμογής και οποιαδήποτε άλλα καθολικά δεδομένα που χρειάζονται πολλά components σε διαφορετικά επίπεδα εμφώλευσης. Η ομάδα του React συνιστά τη χρήση context για δεδομένα που είναι καθολικά για ένα υποδέντρο components, αλλά όχι για ολόκληρη την εφαρμογή.
Σύμφωνα με τα δεδομένα του React Team — Context documentation (2025), η λανθασμένη χρήση του context είναι μία από τις κύριες αιτίες προβλημάτων απόδοσης σε εφαρμογές React. Κάθε αλλαγή τιμής στον Provider προκαλεί re-render όλων των καταναλωτών, ανεξάρτητα από το ποιο μέρος των δεδομένων άλλαξε. Η βελτιστοποίηση μέσω useMemo και ο διαχωρισμός contexts λύνει αυτό το πρόβλημα.
import { createContext, useContext } from 'react';
// Δημιουργία περιβάλλοντος με προεπιλεγμένη τιμή
const ThemeContext = createContext('φως');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Click</button>;
}
Ο μηχανισμός του context στο React υλοποιείται μέσω του προτύπου Provider-Consumer. Το createContext επιστρέφει ένα αντικείμενο με δύο οντότητες: Provider — component που μεταδίδει την τιμή, και το ίδιο το αντικείμενο context, το οποίο χρησιμοποιείται στο useContext. Ο Provider τοποθετείται στο δέντρο components και μεταδίδει την τιμή σε όλα τα θυγατρικά στοιχεία ανεξάρτητα από το βάθος εμφώλευσης.
Όταν το React συναντά μια κλήση useContext, ανεβαίνει στο fiber-δέντρο αναζητώντας τον πλησιέστερο Provider για αυτό το context. Αν βρεθεί Provider, επιστρέφεται η τιμή του. Αν δεν βρεθεί Provider, επιστρέφεται η προεπιλεγμένη τιμή που δόθηκε στο createContext. Αυτή η αναζήτηση γίνεται σε κάθε render, αλλά χάρη στη μνημοποίηση fiber-κόμβων είναι πολύ γρήγορη και δεν επηρεάζει την απόδοση.
Σύμφωνα με το React — Context internals (2024), η εσωτερική υλοποίηση του useContext χρησιμοποιεί μια συνδεδεμένη λίστα hooks, παρόμοια με το useState. Κάθε hook αποθηκεύει μια αναφορά στον fiber-κόμβο, επιτρέποντας στο React να καθορίσει γρήγορα ποιος Provider αντιστοιχεί στο συγκεκριμένο context. Αν ο Provider ενημερώσει την τιμή, το React σημειώνει όλους τους fiber-κόμβους που χρησιμοποιούν αυτό το context για re-render.
Τα components Provider μπορούν να εμφωλευτούν το ένα μέσα στο άλλο, δημιουργώντας μια ιεραρχία contexts. Κάθε θυγατρικός Provider παρακάμπτει την τιμή του γονικού για το υποδέντρο του. Αυτό είναι χρήσιμο όταν σε μία οθόνη χρειάζεται ανοιχτό θέμα, ενώ σε ένα εμφωλευμένο modal χρειάζεται σκοτεινό θέμα. Το useContext επιστρέφει πάντα την τιμή του πλησιέστερου Provider προς τα πάνω στο δέντρο.
const UserContext = createContext(null);
const ThemeContext = createContext('φως');
function App() {
return (
<UserContext.Provider value={{ name: 'Alice' }}>
<ThemeContext.Provider value='dark'>
<Profile />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
Η συνάρτηση createContext(defaultValue) δημιουργεί ένα αντικείμενο context. Η παράμετρος defaultValue χρησιμοποιείται όταν ένα component καλεί το useContext αλλά δεν υπάρχει αντίστοιχος Provider ψηλότερα στο δέντρο. Χωρίς defaultValue, το useContext θα επιστρέψει undefined, το οποίο μπορεί να οδηγήσει σε απροσδόκητα σφάλματα. Συνιστάται να μεταβιβάζεται πάντα μια ουσιαστική προεπιλεγμένη τιμή ή null.
Η δημιουργία ενός προσαρμοσμένου provider είναι ένα συνηθισμένο πρότυπο για την ενθυλάκωση της λογικής του context. Μέσα σε έναν τέτοιο provider αποθηκεύεται η κατάσταση (μέσω useState ή useReducer) και παρέχεται μέσω του value prop του Provider. Αυτό επιτρέπει την απόκρυψη λεπτομερειών υλοποίησης από τα components-καταναλωτές και τη συγκέντρωση της λογικής διαχείρισης του context σε ένα σημείο.
// Προσαρμοσμένος πάροχος με διαχείριση κατάστασης
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback(async (email, pass) => {
const u = await loginApi(email, pass);
setUser(u);
}, []);
return (
<AuthContext.Provider value={{ user, login }}>
{children}
</AuthContext.Provider>
);
}
Στα functional components, το useContext είναι ο μοναδικός τρόπος πρόσβασης στο context. Αντικαθιστά το component Consumer από το παλιό Context API, το οποίο απαιτούσε το πρότυπο render-prop: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. Το useContext κάνει τον κώδικα πιο γραμμικό και ευανάγνωστο, ειδικά όταν εργάζεστε με πολλά contexts σε ένα component.
Όταν χρησιμοποιείτε πολλά contexts σε ένα component, απλώς καλέστε το useContext πολλές φορές για κάθε context. Κάθε κλήση επιστρέφει την τιμή του αντίστοιχου Provider. Η σειρά των κλήσεων δεν έχει σημασία, καθώς κάθε context είναι μια ανεξάρτητη οντότητα. Το React βελτιστοποιεί πολλαπλές κλήσεις μέσω του ίδιου συστήματος fiber-αναφορών.
function Dashboard() {
const { user } = useContext(AuthContext);
const theme = useContext(ThemeContext);
const { locale } = useContext(I18nContext);
return (
<div className={`dashboard-${theme}`}>
<h1>{locale.greeting}, {user.name}</h1>
</div>
);
}
Η επιλογή μεταξύ useContext και Redux εξαρτάται από την κλίμακα και την πολυπλοκότητα της διαχείρισης κατάστασης. Το useContext + useReducer είναι μια ελαφριά εναλλακτική του Redux για μικρές και μεσαίες εφαρμογές. Δεν απαιτεί εγκατάσταση εξωτερικής βιβλιοθήκης, είναι πιο εύκολο στην εκμάθηση και επαρκεί για τις περισσότερες εργασίες. Το Redux δικαιολογείται όταν απαιτείται αυστηρή αρχιτεκτονική με middleware, devtools και immutable ενημερώσεις.
Το κύριο πλεονέκτημα του Redux έναντι του useContext είναι η βελτιστοποίηση re-renders. Από προεπιλογή, όταν αλλάξει η τιμή στον Provider, όλοι οι καταναλωτές του context ανασχεδιάζονται. Το Redux με useSelector και shallowEqual επιτρέπει στα components να εγγράφονται μόνο σε συγκεκριμένα μέρη της κατάστασης, το οποίο μειώνει σημαντικά τον αριθμό των re-renders σε μεγάλες εφαρμογές. Το context μπορεί επίσης να βελτιστοποιηθεί μέσω διαχωρισμού σε πολλά μικρά contexts.
| Κριτήριο | useContext | Redux |
|---|---|---|
| Πολυπλοκότητα | Καμία εξωτερική εξάρτηση | Απαιτεί ρύθμιση store και middleware |
| Re-renders | Όλοι οι καταναλωτές σε κάθε αλλαγή | Μόνο όσοι είναι εγγεγραμμένοι σε συγκεκριμένο slice |
| DevTools | React DevTools | Redux DevTools με time-travel |
| Middleware | Δεν υποστηρίζεται | Redux Thunk, Saga, Observable |
| Πότε να επιλέξεις | Μεσαίες εφαρμογές, 3-5 contexts | Μεγάλες εφαρμογές με πολύπλοκη επιχειρηματική λογική |
Σύμφωνα με τους Redux maintainers — When to use Redux (2024), το 70% των εφαρμογών React δεν χρειάζονται Redux. Αν έχεις λιγότερα από 50 components και η κατάσταση δεν έχει πολύπλοκη λογική με caching, debounce και side-effects — το useContext + useReducer είναι υπεραρκετό. Το Redux προσθέτει boilerplate και πρέπει να χρησιμοποιείται συνειδητά.
Το πιο συνηθισμένο λάθος είναι η αναδημιουργία του value-αντικειμένου σε κάθε render του Provider. Αν μεταβιβάζεις στον Provider value={{ user, login }}, τότε σε κάθε render του Provider δημιουργείται ένα νέο αντικείμενο, το οποίο προκαλεί re-render όλων των καταναλωτών, ακόμα κι αν τα δεδομένα δεν άλλαξαν. Η λύση — κάνε memoization του value μέσω useMemo ή χρησιμοποίησε ξεχωριστά contexts για δεδομένα που αλλάζουν συχνά και σπάνια.
// ❌ Νέο αντικείμενο σε κάθε render — όλοι οι καταναλωτές κάνουν re-render
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>
// ✅ Τιμή με memoization — re-render μόνο όταν αλλάζει ο user ή το login
const authValue = useMemo(() => ({ user, login }), [user, login]);
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>
Για να λύσεις το πρόβλημα του “μεγάλου context”, χώρισε την καθολική κατάσταση σε λογικές ομάδες: AuthContext, ThemeContext, I18nContext. Κάθε context είναι υπεύθυνο για τον τομέα του και ενημερώνεται ανεξάρτητα. Αυτό είναι απλούστερο από το να προσπαθείς να βελτιστοποιήσεις ένα γιγαντιαίο context μέσω useMemo και δίνει πιο προβλέψιμη συμπεριφορά re-renders.
Συχνές ερωτήσεις
Ναι, αν μεταβιβάσεις μια συνάρτηση-μεταλλακτή στο value του Provider. Το τυπικό πρότυπο είναι η αποθήκευση της κατάστασης στον Provider και η μεταβίβαση τόσο δεδομένων όσο και συναρτήσεων για την ενημέρωσή τους μέσω του useContext. Τα θυγατρικά components καλούν αυτές τις συναρτήσεις και η αλλαγή κατάστασης στον Provider ενημερώνει αυτόματα όλους τους καταναλωτές. Αυτή είναι η βασική εναλλακτική του Redux για μικρές εφαρμογές.
Για απλά σενάρια, το useContext είναι ταχύτερο λόγω απουσίας overhead από το store και middleware. Αλλά σε συχνές ενημερώσεις με μεγάλο αριθμό καταναλωτών, το Redux υπερέχει, καθώς οι επιλογείς του (useSelector) εγγράφονται σε συγκεκριμένα μέρη της κατάστασης, ενώ το useContext ανασχεδιάζει όλους τους καταναλωτές σε κάθε αλλαγή. Για εφαρμογές με υψηλή συχνότητα ενημερώσεων (κινουμένα σχέδια, real-time) η επιλογή πρέπει να γίνεται υπέρ του Redux ή εξειδικευμένων βιβλιοθηκών.
Όχι. Το useContext, όπως όλα τα hooks, μπορεί να κληθεί μόνο μέσα σε ένα functional component React ή ένα custom hook. Αν χρειάζεται να λάβεις την τιμή του context σε μια κανονική συνάρτηση (π.χ. σε μια βοηθητική συνάρτηση ή υπηρεσία), μεταβίβασέ την ως παράμετρο από το component ή χρησιμοποίησε ένα ξεχωριστό module με καθολική κατάσταση εκτός React.
Ητυποποίηση του context στο TypeScript — καθορισμός τύπου στο createContext: createContext<AuthContextType | null>(null). Αυτό εγγυάται ότι το useContext(AuthContext) επιστρέφει τιμή του σωστού τύπου. Ένα βολικό πρότυπο είναι η δημιουργία ενός custom hook useAuth που καλεί το useContext, ελέγχει για null και εκτοξεύει ένα κατανοητό σφάλμα: «Το useAuth πρέπει να χρησιμοποιείται εντός του AuthProvider».
Η πιο συνηθισμένη αιτία — το component-καταναλωτής βρίσκεται εκτός του αντίστοιχου Provider. Έλεγξε ότι ο Provider περιβάλλει ολόκληρο το υποδέντρο στο οποίο χρησιμοποιείται το useContext. Η δεύτερη αιτία — ο Provider έχει μεταβιβαστεί σε διαφορετικό αντικείμενο context: ο προγραμματιστής δημιουργεί context με κλήση createContext, αλλά χρησιμοποιεί useContext με διαφορετικό στιγμιότυπο createContext.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης