React Hooks — είναι συναρτήσεις που επιτρέπουν τη χρήση κατάστασης, παρενεργειών, περιβάλλοντος και άλλων δυνατοτήτων του React σε λειτουργικά components, χωρίς τη σύνταξη κλάσεων. Παρουσιάστηκαν στο React 16.8 και άλλαξαν ριζικά την προσέγγιση ανάπτυξης, αντικαθιστώντας σύνθετα μοτίβα όπως HOC και render props. Σύμφωνα με το React, 2024, υπάρχουν 15 ενσωματωμένα hooks που καλύπτουν όλα τα σενάρια διαχείρισης components.
Κύρια
React Hooks — ένα σύνολο ενσωματωμένων συναρτήσεων στο React που επιτρέπουν τη ‘σύνδεση’ με τους εσωτερικούς μηχανισμούς του πλαισίου χωρίς τη δημιουργία κλάσεων. Πριν από το React 16.8, τα λειτουργικά components μπορούσαν μόνο να λαμβάνουν props και να επιστρέφουν JSX — οποιαδήποτε σύνθετη λογική απαιτούσε class component.
Το πρόβλημα των class components — ο διαχωρισμός συσχετισμένης λογικής. Ο κώδικας που ανήκε σε μία οντότητα (π.χ. συνδρομή WebSocket) αναγκαστικά χωριζόταν μεταξύ componentDidMount, componentDidUpdate και componentWillUnmount. Τα hooks το λύνουν: όλη η λογική ομαδοποιείται σε ένα useEffect.
Το δεύτερο πρόβλημα — επαναχρησιμοποίηση λογικής. Για επαναχρησιμοποίηση λογικής κατάστασης μεταξύ class components έπρεπε να εφαρμοστούν HOC (components ανώτερης τάξης) ή render props — και τα δύο μοτίβα δημιουργούσαν επιπλέον φωλιάσματα στο δέντρο components. Τα custom hooks λύνουν αυτό το πρόβλημα χωρίς φωλιάσματα.
useState — το πιο βασικό hook. Δέχεται μια αρχική τιμή και επιστρέφει ένα tuple: την τρέχουσα τιμή και μια συνάρτηση για την ενημέρωσή της. Σε αντίθεση με το this.setState στις κλάσεις, το useState δεν συγχωνεύει αντικείμενα — η νέα κατάσταση αντικαθιστά πλήρως την προηγούμενη για μια συγκεκριμένη κλήση useState.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
Ο setter του useState μπορεί να δεχτεί μια συνάρτηση που λαμβάνει την προηγούμενη τιμή και επιστρέφει μια νέα. Η λειτουργική μορφή είναι υποχρεωτική όταν η νέα κατάσταση υπολογίζεται από την προηγούμενη και είναι πιθανές ομαδικές κλήσεις setState — κάθε κλήση θα λάβει την ενημερωμένη προηγούμενη τιμή.
useEffect — hook για την εκτέλεση παρενεργειών σε λειτουργικά components. Δέχεται δύο ορίσματα: μια συνάρτηση-επίδραση και έναν πίνακα εξαρτήσεων. Το React εκτελεί την επίδραση μετά από κάθε render, αλλά όταν καθορίζονται εξαρτήσεις — μόνο όταν οποιαδήποτε από αυτές αλλάξει.
Ένας κενός πίνακας εξαρτήσεων [] σημαίνει ότι η επίδραση εκτελείται μία φορά μετά το πρώτο render (ανάλογο του componentDidMount). Η επιστροφή μιας συνάρτησης από την επίδραση είναι το cleanup, που εκτελείται κατά την αποπροσάρτηση (ανάλογο του componentWillUnmount) και πριν από την εκ νέου εκτέλεση της επίδρασης.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Cleanup - κλείσιμο socket
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
Οι εξαρτήσεις του useEffect — είναι η μοναδική σύνδεση μεταξύ της επίδρασης και του εξωτερικού κόσμου. Εάν στην επίδραση χρησιμοποιείται μια μεταβλητή από props ή κατάσταση, πρέπει να βρίσκεται στον πίνακα εξαρτήσεων. Η αγνόηση αυτού του κανόνα είναι πηγή σφαλμάτων με παλιά δεδομένα (stale closure).
useContext — hook για την ανάγνωση της τιμής περιβάλλοντος. Δέχεται το αντικείμενο περιβάλλοντος που δημιουργήθηκε με το React.createContext και επιστρέφει την τρέχουσα τιμή. Εάν το περιβάλλον αλλάξει, το component αναπροσαρμόζεται αυτόματα με τη νέα τιμή.
Πριν από την εμφάνιση του useContext, η πρόσβαση στο περιβάλλον σε λειτουργικά components απαιτούσε περιτύλιγμα σε Context.Consumer, που δημιουργούσε φώλιασμα. useContext εξαλείφει πλήρως το Consumer — αρκεί να καλέσετε το hook οπουδήποτε στο λειτουργικό component.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('φως');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
Το useContext δεν αντικαθιστά το Redux ή την καθολική κατάσταση. Το React συνιστά τη χρήση περιβάλλοντος για σπάνια μεταβαλλόμενες τιμές — θέμα, γλώσσα, εξουσιοδότηση. Για συχνά ενημερωμένα δεδομένα, κάθε επαναληπτικό render θα υπολογίζει εκ νέου όλους τους καταναλωτές του περιβάλλοντος, γεγονός που μπορεί να μειώσει την απόδοση.
useReducer — εναλλακτική λύση για το useState για καταστάσεις με σύνθετη λογική ενημέρωσης. Δέχεται μια συνάρτηση reducer και μια αρχική κατάσταση, επιστρέφει την τρέχουσα κατάσταση και μια συνάρτηση dispatch. Ο reducer λαμβάνει την τρέχουσα κατάσταση και την ενέργεια, επιστρέφει μια νέα κατάσταση — μια καθαρή συνάρτηση χωρίς παρενέργειες.
Το useReducer είναι ιδιαίτερα χρήσιμο όταν πολλές ενέργειες (set, reset, increment, addItem) διαχειρίζονται μία κατάσταση. Αντί για πολλαπλά useState με διαφορετικούς setter — ένας reducer με σαφείς τύπους ενεργειών. Αυτό καθιστά τη λογική προβλέψιμη και εύκολα ελέγξιμη.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef δημιουργεί ένα μεταβλητό αντικείμενο που διατηρείται μεταξύ render. Η αλλαγή του current δεν προκαλεί επαναληπτικό render. Κύρια εφαρμογή — πρόσβαση σε στοιχεία DOM και αποθήκευση μεταβλητών τιμών (χρονοδιακόπτες, προηγούμενες τιμές, στιγμιότυπα βιβλιοθηκών).
useMemo απομνημονεύει το αποτέλεσμα μιας συνάρτησης. Υπολογίζει ξανά την τιμή μόνο όταν αλλάξει μία από τις εξαρτήσεις. Χρησιμοποιήστε για ακριβούς υπολογισμούς — φιλτράρισμα μεγάλων πινάκων, ταξινόμηση, μορφοποίηση δεδομένων. useCallback — ειδική περίπτωση του useMemo για απομνημόνευση συναρτήσεων.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Αυτόματη εστίαση κατά την προσάρτηση
useEffect(() => {
inputRef.current?.focus();
}, []);
// Απομνημόνευση φιλτραρισμένου αποτελέσματος
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Απομνημόνευση callback
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Σημαντικός κανόνας: useRef, useMemo και useCallback — εργαλεία βελτιστοποίησης, όχι υποχρεωτικές κατασκευές. Μην τυλίγετε τα πάντα σε useMemo — μετρήστε πρώτα την απόδοση. Η πρόωρη βελτιστοποίηση περιπλέκει τον κώδικα. Το React κάνει render γρήγορα τα components· το useMemo χρειάζεται μόνο όταν οι υπολογισμοί είναι πραγματικά ακριβοί (O(n²) και άνω).
Το React εισήγαγε δύο αυστηρούς κανόνες για τα hooks. Πρώτος: καλέστε τα hooks μόνο στο ανώτερο επίπεδο — όχι εντός συνθηκών, βρόχων ή ένθετων συναρτήσεων. Δεύτερος: καλέστε τα hooks μόνο από λειτουργικά components React ή custom hooks. Το πρόσθετο ESLint eslint-plugin-react-hooks ελέγχει αυτόματα και τους δύο κανόνες.
Το πιο συχνό λάθος — παραβίαση της σειράς κλήσης των hooks. Το React βασίζεται στη σειρά κλήσης των hooks μεταξύ render. Εάν ένα hook βρίσκεται εντός συνθήκης και δεν κλήθηκε σε ένα από τα render, όλα τα επόμενα hooks μετατοπίζονται, προκαλώντας σφάλματα.
| Σφάλμα | Τι συμβαίνει | Λύση |
|---|---|---|
| Hook σε συνθήκη | Η σειρά των hooks σπάει | Μεταφέρετε τη λογική της συνθήκης εντός του hook |
| Stale closure | Η επίδραση χρησιμοποιεί παλιά τιμή | Προσθέστε όλες τις εξαρτήσεις στον πίνακα |
| Ατέρμονος βρόχος | useEffect χωρίς εξαρτήσεις αλλάζει την κατάσταση | Καθορίστε εξαρτήσεις ή χρησιμοποιήστε useReducer |
| Αλλαγή ref σε render | Παρενέργεια στο σώμα της συνάρτησης | Μεταφέρετε μεταλλάξεις στο useEffect |
Το δεύτερο συχνό λάθος — stale closure στο useEffect. Εάν στην επίδραση χρησιμοποιείται μια μεταβλητή που δεν καθορίζεται στις εξαρτήσεις, η επίδραση ‘συλλαμβάνει’ την τιμή της τη στιγμή δημιουργίας του κλεισίματος. Λύση — συμπεριλάβετε πάντα όλες τις χρησιμοποιούμενες μεταβλητές στον πίνακα εξαρτήσεων. Εάν υπάρχουν πάρα πολλές εξαρτήσεις, διαχωρίστε την επίδραση σε πολλά μέρη.
Συχνές Ερωτήσεις
Το React βασίζεται στη σειρά κλήσης των hooks μεταξύ render. Εάν ένα hook κλήθηκε σε συνθήκη και δεν λειτούργησε σε ένα από τα render, όλα τα επόμενα hooks ‘γλιστρούν’ — η κατάστασή τους ανακατεύεται. Ο κανόνας ESLint hooks/exhaustive-deps προειδοποιεί γι’ αυτό.
useLayoutEffect είναι σύγχρονο και εκτελείται πριν το πρόγραμμα περιήγησης σχεδιάσει τις αλλαγές. useEffect είναι ασύγχρονο — μετά τη σχεδίαση. Χρησιμοποιήστε το useLayoutEffect μόνο όταν πρέπει να μετρήσετε ή να αλλάξετε το DOM πριν ο χρήστης δει τις αλλαγές (θέση popover, επαναφορά κύλισης).
Χρησιμοποιήστε AbortController μέσα στο useEffect. Δημιουργήστε έναν controller, περάστε το signal στο fetch και στη συνάρτηση cleanup καλέστε το controller.abort(). Για παλιά προγράμματα περιήγησης αρκεί μια σημαία cancelled: return () => { cancelled = true; } με έλεγχο πριν από το setState.
Custom hook — μια συνάρτηση JavaScript με πρόθεμα use που χρησιμοποιεί ενσωματωμένα hooks. Δημιουργήστε το όταν η ίδια λογική με hooks επαναλαμβάνεται σε δύο ή περισσότερα components. Παραδείγματα: useDebounce, useLocalStorage, useMediaQuery. Τα custom hooks αντικαθιστούν τα HOC και τα render props.
Στο Strict Mode, το React σκόπιμα προσαρτά και αποπροσαρτά το component δύο φορές σε λειτουργία ανάπτυξης για να βρει σφάλματα με μη καθαρισμένες επιδράσεις. Εάν το cleanup είναι γραμμένο σωστά, η διπλή κλήση δεν προκαλεί προβλήματα. Σε λειτουργία παραγωγής αυτό δεν συμβαίνει.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης