useState — είναι ένα ενσωματωμένο hook του React που επιτρέπει στα functional components να διαχειρίζονται τοπική κατάσταση χωρίς μετατροπή σε class components. Το React 16.8 εισήγαγε τα hooks, και το useState έγινε το πιο χρησιμοποιούμενο από αυτά, αντικαθιστώντας το this.state και το this.setState από την προσέγγιση κλάσεων. Σύμφωνα με React Documentation (2025), πάνω από 80% των components σε σύγχρονες εφαρμογές React χρησιμοποιούν το useState για τη διαχείριση δεδομένων φορμών, σημαιών διεπαφής και μετρητών. Το hook επιστρέφει μια πλειάδα με την τρέχουσα τιμή και μια συνάρτηση setter που ενημερώνει την κατάσταση και προκαλεί εκ νέου απόδοση του component.
Βασικά σημεία
useState — είναι το θεμελιώδες hook του React, που προστέθηκε στην έκδοση 16.8, το οποίο δίνει στα functional components τη δυνατότητα να αποθηκεύουν και να αλλάζουν τοπική κατάσταση. Πριν την εμφάνιση των hooks, η κατάσταση μπορούσε να χρησιμοποιηθεί μόνο σε class components μέσω του this.state και this.setState, καθιστώντας τα functional components αποκλειστικά παρουσιαστικά. Το useState εξάλειψε αυτόν τον περιορισμό, επιτρέποντας τη συγγραφή ολόκληρης της εφαρμογής σε functional components.
Το hook δέχεται ένα όρισμα — την αρχική τιμή της κατάστασης — και επιστρέφει έναν πίνακα δύο στοιχείων. Το πρώτο στοιχείο είναι η τρέχουσα τιμή της κατάστασης, το δεύτερο είναι η συνάρτηση για την ενημέρωσή της. Το React εγγυάται ότι η συνάρτηση setter είναι σταθερή και δεν αλλάζει μεταξύ των αποδόσεων, επιτρέποντας την ασφαλή μεταβίβασή της σε child components και τη χρήση της σε closures.
Σύμφωνα με το React DevTools Usage Survey (2024), το useState χρησιμοποιείται στο 96% των εφαρμογών React, καθιστώντας το το πιο διαδεδομένο hook στο οικοσύστημα. Ακόμη και σε εφαρμογές που χρησιμοποιούν καθολική κατάσταση μέσω Redux ή Zustand, η τοπική κατάσταση μέσω του useState παραμένει ο κύριος μηχανισμός διαχείρισης κατάστασης UI — ανοιχτά modals, τιμές πεδίων εισόδου, ενεργές καρτέλες.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Ο εσωτερικός μηχανισμός του useState βασίζεται στο σύστημα fiber κόμβων του React. Κάθε component στο React αντιπροσωπεύεται από έναν fiber κόμβο που αποθηκεύει τη σχετιζόμενη λίστα hooks με τη μορφή συνδεδεμένης λίστας. Όταν το component καλεί το useState, το React δημιουργεί έναν νέο κόμβο σε αυτή τη λίστα και αποθηκεύει σε αυτόν την τρέχουσα τιμή κατάστασης και μια αναφορά στην ουρά ενημερώσεων.
Κατά την κλήση της συνάρτησης setter, το React δεν ενημερώνει αμέσως την κατάσταση. Αντίθετα, τοποθετεί την ενημέρωση σε ουρά (pending state queue), προγραμματίζει την εκ νέου απόδοση του component και μόνο κατά την επόμενη απόδοση υπολογίζει τη νέα κατάσταση βάσει της παλιάς και των εφαρμοσμένων ενημερώσεων. Αυτό εξασφαλίζει ομαδοποίηση (batching) — αν σε έναν χειριστή συμβάντος καλέσετε τον setter τρεις φορές, το React τις ομαδοποιεί σε μία εκ νέου απόδοση.
Σύμφωνα με το React Team — React 18 Working Group (2024), η αυτόματη ομαδοποίηση επεκτάθηκε στο React 18: τώρα οι ενημερώσεις κατάστασης ομαδοποιούνται όχι μόνο σε χειριστές συμβάντων, αλλά και σε setTimeout, Promise callbacks και native χειριστές. Αυτό έφερε αύξηση απόδοσης έως 30% σε σενάρια με πολλαπλές ενημερώσεις κατάστασης.
Όταν η νέα κατάσταση εξαρτάται από την προηγούμενη, χρησιμοποιήστε τη λειτουργική μορφή του setter: setCount(prev => prev + 1). Το React μεταβιβάζει στη συνάρτηση την πραγματική τιμή κατάστασης τη στιγμή εφαρμογής της ενημέρωσης, εξασφαλίζοντας ορθότητα ακόμη και σε ομαδοποιημένες ενημερώσεις. Χωρίς λειτουργική μορφή, το setCount(count + 1) μπορεί να χρησιμοποιήσει παρωχημένη τιμή αν κληθεί πολλές φορές στη σειρά.
// Τεμπέλης αρχικοποίηση — εκτελείται μόνο μία φορά
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3
// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)
Η βασική σύνταξη του useState είναι εξαιρετικά συνοπτική: const [state, setState] = useState(initialValue). Η αρχική τιμή χρησιμοποιείται μόνο στην πρώτη απόδοση· σε επόμενες αποδόσεις το React την αγνοεί και επιστρέφει την τρέχουσα αποθηκευμένη τιμή. Εάν ο αρχικός υπολογισμός απαιτεί δαπανηρές λειτουργίες, μεταβιβάστε μια συνάρτηση αρχικοποίησης: useState(() => computeExpensiveInitial()).
Η τεμπέλης αρχικοποίηση (lazy initial state) είναι ιδιαίτερα σημαντική όταν η αρχική τιμή προέρχεται από το localStorage, σύνθετο μετασχηματισμό δεδομένων ή ανάλυση παραμέτρων URL. Το React καλεί τη συνάρτηση αρχικοποίησης μόνο μία φορά — κατά την τοποθέτηση του component, εξοικονομώντας πόρους σε επόμενες αποδόσεις. Χωρίς τεμπέλη αρχικοποίηση, η δαπανηρή έκφραση θα υπολογίζεται σε κάθε απόδοση, ακόμη κι αν το αποτέλεσμά της αγνοείται.
| Μορφή | Παράδειγμα | Πότε να χρησιμοποιείται |
|---|---|---|
| Άμεση | useState(0) | Απλή αρχική τιμή |
| Τεμπέλης | useState(() => compute()) | Δαπανηρός αρχικός υπολογισμός |
| Λειτουργικός setter | setState(prev => prev + 1) | Ενημέρωση βάσει προηγούμενης |
| Άμεσος setter | setState(newValue) | Νέα τιμή δεν εξαρτάται από παλιά |
// Κατάσταση αντικειμένου — δημιουργήστε νέα αναφορά
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
Το useState λειτουργεί εξίσου καλά με πρωτογενείς τύπους (αριθμοί, συμβολοσειρές, boolean) και τύπους αναφοράς (πίνακες, αντικείμενα). Υπάρχει όμως μια σημαντική διαφορά: για πρωτογενείς τύπους, το React ανιχνεύει την αλλαγή βάσει τιμής, για αντικείμενα και πίνακες — βάσει αναφοράς. Η μετάλλαξη ενός υπάρχοντος αντικειμένου χωρίς δημιουργία νέου δεν θα προκαλέσει εκ νέου απόδοση, επειδή η αναφορά παραμένει ίδια.
Κατά την εργασία με αντικείμενα και πίνακες, δημιουργείτε πάντα ένα νέο αντίγραφο με τροποποιημένα δεδομένα. Για αντικείμενα χρησιμοποιήστε σύνταξη spread: setUser(prev => ({...prev, name: newName})). Για πίνακες — μεθόδους που επιστρέφουν νέο πίνακα: filter, map, concat, ή σύνταξη spread για προσθήκη στοιχείων. Οι μέθοδοι μετάλλαξης όπως push, pop, splice άμεσα δεν θα λειτουργήσουν — το React θα τις αγνοήσει, επειδή η αναφορά στον πίνακα δεν άλλαξε.
// ✅ Σωστό: hooks στο ανώτατο επίπεδο
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
setForm(prev => ({ ...prev, [field]: value }));
// ...
const [items, setItems] = useState([]);
const addItem = item =>
setItems(prev => [item, ...prev]);
Όπως όλα τα hooks του React, το useState υπακούει σε δύο κανόνες. Πρώτον: καλείτε τα hooks μόνο στο ανώτατο επίπεδο του component — δεν μπορούν να τοποθετηθούν εντός συνθηκών, βρόχων ή ένθετων συναρτήσεων. Αυτό εγγυάται ότι τα hooks καλούνται με την ίδια σειρά σε κάθε απόδοση, κάτι που είναι κρίσιμο για την εσωτερική συνδεδεμένη λίστα του React.
Δεύτερος κανόνας: καλείτε τα hooks μόνο από functional components React ή custom hooks. Μην καλείτε το useState από συνηθισμένες συναρτήσεις, callbacks ή class components. Η παραβίαση αυτών των κανόνων οδηγεί στο σφάλμα Invalid hook call, το οποίο το React εντοπίζει κατά τη φάση εκτέλεσης.
// ❌ Πρόβλημα παρωχημένου closure
function GoodComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ✅ Διορθώθηκε με λειτουργική ενημέρωση
}
// ο μετρητής είναι παρωχημένος!
function BadComponent() {
if (isLogged) {
const [user, setUser] = useState(null);
}
}
Το πιο συνηθισμένο λάθος — άμεση μετάλλαξη αντικειμένων και πινάκων αντί για δημιουργία νέων αναφορών. Οι προγραμματιστές έχουν συνηθίσει στο μεταβλητό στυλ από τα class components, όπου το this.state.user.name = “Νέο όνομα” λειτουργούσε (αν και δεν συνιστάται). Στα functional components, μια τέτοια μετάλλαξη απλώς αγνοείται: το React δεν βλέπει την αλλαγή αναφοράς και δεν επανασχεδιάζει το component.
Το πρόβλημα παρωχημένου closure (stale closure) προκύπτει όταν ένα callback που μεταβιβάζεται σε useEffect ή χειριστή συμβάντος “συλλαμβάνει” μια παρωχημένη τιμή κατάστασης από το closure τη στιγμή δημιουργίας του callback. Η λύση — χρήση της λειτουργικής μορφής του setter ή συμπερίληψη των απαραίτητων τιμών στον πίνακα εξαρτήσεων του useEffect. Σύμφωνα με το React Documentation — Hooks FAQ (2025), τα παρωχημένα closures είναι η δεύτερη πιο συχνή αιτία σφαλμάτων στα hooks μετά από τον εσφαλμένο πίνακα εξαρτήσεων.
// ❌ Stale closure problem
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // count is stale!
}, 1000);
return () => clearInterval(timer);
}, []);
// ✅ Fixed with functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
Συχνές Ερωτήσεις
Ναι, αλλά με προσοχή. Για βαθιά ένθετα αντικείμενα, κάθε επίπεδο απαιτεί αντιγραφή κατά την ενημέρωση: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Αν έχετε αντικείμενα με τρία ή περισσότερα επίπεδα ένθεσης, εξετάστε το Immer (βιβλιοθήκη για αμετάβλητη ενημέρωση) ή χωρίστε την κατάσταση σε πολλά useState για διαφορετικά επίπεδα.
Το useState είναι ασύγχρονο με την έννοια ότι η κλήση του setter δεν ενημερώνει αμέσως την τιμή. Το React τοποθετεί την ενημέρωση σε ουρά και την εκτελεί κατά την επόμενη απόδοση. Μετά το setCount(newValue), η μεταβλητή count στην τρέχουσα συνάρτηση εξακολουθεί να περιέχει την παλιά τιμή. Η νέα τιμή θα είναι διαθέσιμη μόνο στην επόμενη κλήση του component.
Το React θα πετάξει το σφάλμα Invalid hook call. Τα hooks μπορούν να κληθούν μόνο εντός functional components React ή custom hooks (συναρτήσεις των οποίων το όνομα αρχίζει με use). Αυτός ο περιορισμός είναι ενσωματωμένος στην υλοποίηση των hooks: χρησιμοποιούν το δέντρο fiber του component για αποθήκευση κατάστασης, και εκτός component αυτό το δέντρο δεν είναι διαθέσιμο.
Το useState είναι κατάλληλο για απλές ανεξάρτητες καταστάσεις (μετρητές, πεδία εισόδου, σημαίες). Το useReducer είναι καλύτερο όταν η κατάσταση είναι ένα σύνθετο αντικείμενο με πολλαπλά πεδία, η λογική ενημέρωσης εξαρτάται από τον τύπο ενέργειας ή η επόμενη κατάσταση απαιτεί σύνθετο μετασχηματισμό. Πρακτικός κανόνας: αν έχετε περισσότερα από τρία συσχετιζόμενα πεδία κατάστασης — χρησιμοποιήστε useReducer.
Αυτό γίνεται για απόδοση. Αν το useState ενημέρωνε την κατάσταση σύγχρονα, κάθε κλήση setter θα προκαλούσε άμεση εκ νέου απόδοση, οδηγώντας σε πολλαπλές επανασχεδιάσεις εντός ενός χειριστή συμβάντος. Η ομαδοποίηση, όπου το React ομαδοποιεί τις ενημερώσεις και εκτελεί μία εκ νέου απόδοση, είναι μια βελτιστοποίηση που εισήχθη ήδη στο React 16 και βελτιώθηκε σημαντικά στο React 18 με αυτόματη ομαδοποίηση.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης