Μια βαθύτερη ματιά στα πιο δημοφιλή άγκιστρα React
Pawel Rybczynski
Software Engineer
Κατά τη διάρκεια πολλών συνεντεύξεων, παρατήρησα ότι ακόμη και έμπειροι προγραμματιστές έχουν πρόβλημα με τη διάκριση των Hooks, για να μην αναφέρω τις πιο προηγμένες δυνατότητές τους. Έτσι, θα προσπαθήσω να εξηγήσω σε αυτό το άρθρο πώς πρέπει να χρησιμοποιούνται τα Hooks.
Τα πιο σημαντικά πράγματα που πρέπει να θυμάστε σχετικά με τα άγκιστρα:
μπορούν να χρησιμοποιηθούν μόνο σε στοιχεία συναρτήσεων - τα στοιχεία κλάσεων έχουν δική τους υλοποίηση του κύκλου ζωής,
αρχίζουν πάντα με χρήση;
μπορείτε να χρησιμοποιήσετε όσα Hooks θέλετε, αλλά πρέπει να θυμάστε ότι η χρήση τους έχει αντίκτυπο στη συνολική απόδοση,
πρέπει να εκτελούνται με την ίδια σειρά σε κάθε απόδοση... αλλά γιατί; Ας δούμε ένα παράδειγμα:
Αρχικά, θα λάβετε μια προειδοποίηση από το eslint:
<code>srcFunctionComponent.js
Γραμμή 11:5: React Το άγκιστρο "useEffect" καλείται υπό όρους. <strong>React Άγκιστρα</strong> πρέπει να καλούνται με την ίδια ακριβώς σειρά σε κάθε στοιχείο render .eslintreact-hooks/rules-of-hooks
Όπως μπορείτε να δείτε, είναι μόνο μια προειδοποίηση eslint, οπότε μπορείτε να την απενεργοποιήσετε προσθέτοντας μια εντολή από κάτω στην κορυφή του FunctionComponent
/* eslint-disable react-hooks/rules-of-hooks */
και θα λειτουργήσει, αλλά μόνο μέχρι να εκπληρώσουμε τον όρο που τρέχει το άγκιστρο μας. Το επόμενο πράγμα που θα δούμε είναι αυτό το σφάλμα.
Σφάλμα χωρίς αιτία: Rendered more hooks than during the previous render.
React 5
FunctionComponent FunctionComponent.js:11
React 12
unstable_runWithPriority scheduler.development.js:468
React 17
js index.js:7
js main.chunk.js:905
Webpack 7
react-dom.development.js:15162
Γιατί συμβαίνει αυτό; Το React βασίζεται στη σειρά με την οποία καλούνται τα Hooks, καθώς το React δεν θα ήξερε τι να επιστρέψει για το useEffect επειδή δεν υπήρχε τέτοιο Hook στη γραμμή για να ελέγξει.
Θυμηθείτε, το eslint είναι ένα ισχυρό εργαλείο, που μας βοηθά να εντοπίσουμε πολλά πιθανά σφάλματα και λάθη. Η απενεργοποίηση των προειδοποιήσεών του είναι κάτι επικίνδυνο, ελέγχετε πάντα αν η αγνόηση της προειδοποίησης μπορεί να προκαλέσει κατάρρευση της εφαρμογής.
useState
Πιθανόν να ξέρετε πώς φαίνεται 😉
const [value, setValue] = useState(0),
Έτσι, έχετε 4 στοιχεία: κατάσταση (αντιδραστική τιμή), συνάρτηση ενημέρωσης (setter), πραγματικό άγκιστρο (συνάρτηση) και προαιρετική αρχική τιμή. Γιατί επιστρέφει έναν πίνακα; Επειδή μπορούμε να την αναδομήσουμε όπως θέλουμε.
Τώρα θέλω να επικεντρωθώ στο τελευταίο στοιχείο - την αρχική τιμή. Υπάρχουν δύο τρόποι για να περάσετε την αρχική κατάσταση:
Με σκληρά κωδικοποιημένη τιμή ή κάτι τέτοιο - το οποίο θα καλείται σε κάθε απόδοση
const [value, setValue] = useState(0),
Με μια έκδοση συνάρτησης. Είναι πραγματικά χρήσιμο αν θέλουμε να εκτελέσουμε την αρχική κατάσταση μόνο μία φορά, κατά την πρώτη απόδοση. Ίσως χρειάζεται να κάνετε πολλούς πολύπλοκους υπολογισμούς για να λάβετε την αρχική κατάσταση; Θα μειώσει όμορφα το κόστος των πόρων, ναι!
Ένα άλλο πράγμα που μπορεί να δημιουργήσει σφάλματα στη ροή της εφαρμογής: πιθανότατα γνωρίζετε πώς να ενημερώνετε μια κατάσταση, σωστά;
setValue(1),
Σωστά... αλλά τι γίνεται αν θέλω να ενημερώσω την κατάσταση με βάση μια προηγούμενη κατάσταση;
setValue(value + 1),
Ναι... Αλλά όχι... Τι γίνεται αν προσπαθήσετε να καλέσετε τη συνάρτηση setter δύο φορές, τη μία μετά την άλλη; Ο συνιστώμενος τρόπος για να ενημερώσετε μια κατάσταση με βάση την προηγούμενη κατάσταση είναι να χρησιμοποιήσετε μια συνάρτηση. Αυτό εγγυάται ότι αναφέρεστε στην προηγούμενη κατάσταση
Αυτό το Hook δέχεται 2 ορίσματα (το δεύτερο είναι προαιρετικό) και το χρησιμοποιούμε για να χειριζόμαστε τις παρενέργειες. Και ανάλογα με το τι περνάμε ως δεύτερο όρισμα, το Hook θα κληθεί διαφορετικά:
χωρίς δεύτερο όρισμα - κάθε απόδοση
useEffect(() => {
doSomething(),
});
άδειος πίνακας - μόνο στην πρώτη απόδοση
useEffect(() => {
doSomething(),
}, []);
πίνακα με εξαρτήσεις - κάθε φορά που αλλάζει η τιμή στον πίνακα εξαρτήσεων
Με το useEffect, μπορούμε να χρησιμοποιήσουμε κάτι που ονομάζεται cleanup. Για ποιο λόγο; Είναι πολύ χρήσιμο, αλλά νομίζω ότι είναι καλύτερο για τον καθαρισμό των ακροατών συμβάντων. Ας πούμε ότι θέλετε να δημιουργήσετε έναν ακροατή συμβάντων που εξαρτάται από κάποια κατάσταση. Δεν θέλετε να προσθέτετε έναν νέο ακροατή συμβάντων σε κάθε αλλαγή κατάστασης, γιατί μετά από μερικά renders θα υπάρχουν τόσοι πολλοί ακροατές που θα επηρεάζουν την απόδοση της εφαρμογής. Ένας πολύ καλός τρόπος για να αποφύγετε τέτοια πράγματα είναι να χρησιμοποιήσετε τη λειτουργία καθαρισμού. Πώς να το κάνετε; Απλά προσθέστε μια συνάρτηση επιστροφής στο useEffect.
Επειδή βρίσκεται μέσα στο useEffect Hook, η επιστροφή καλείται ανάλογα με τον πίνακα εξαρτήσεων - σε κάθε απόδοση, μόνο στην πρώτη απόδοση ή όταν αλλάζει η τιμή στον πίνακα εξαρτήσεων. Αλλά όταν το συστατικό αποσυνδέεται, το cleaning θα κληθεί στο δεύτερο όρισμα, ό,τι κι αν γίνει. Η επιστροφή κωδικός καλείται πριν από τον πραγματικό κώδικα από το Hook. Είναι πολύ λογικό - πρώτα καθαρίζετε το παλιό και στη συνέχεια δημιουργείτε ένα νέο. Σωστά;
Στην αρχή, φαίνεται εντάξει. Λαμβάνετε κάποια δεδομένα, και όταν έρθουν τα δεδομένα, ενημερώνετε την κατάσταση. Και εδώ είναι η παγίδα:
Μερικές φορές θα λάβετε μια τέτοια προειδοποίηση: Δεν μπορεί να πραγματοποιηθεί ενημέρωση κατάστασης React σε ένα μη προσαρτημένο στοιχείο. Αυτό είναι ένα no-op, αλλά υποδεικνύει μια διαρροή μνήμης στην εφαρμογή σας. Για να το διορθώσετε, ακυρώστε όλες τις συνδρομές και τις ασύγχρονες εργασίες σε μια συνάρτηση καθαρισμού useEffect.
Ο λόγος είναι ότι το στοιχείο μπορεί να αποσυνδεθεί εν τω μεταξύ, αλλά η εφαρμογή θα εξακολουθεί να προσπαθεί να ενημερώσει την κατάσταση του στοιχείου αυτού μετά την εκπλήρωση της υπόσχεσης. Πώς να το αντιμετωπίσετε; Πρέπει να ελέγξετε αν το συστατικό υπάρχει.
Σημείωση: Υπάρχει ένα πολύ παρόμοιο Hook => useLayoutEffect() - το callback εκτελείται μετά την απόδοση του συστατικού αλλά πριν την οπτική ενημέρωση του dom. Είναι χρήσιμο όταν εργάζεστε με την getBoundingClientRect(), αλλά θα πρέπει να χρησιμοποιείτε το useEffect από προεπιλογή. Γιατί; Επειδή μπορεί να μπλοκάρει τις οπτικές ενημερώσεις - όταν έχετε έναν πολύπλοκο κώδικα μέσα στο effect Hook σας.
useContext
Για ποιο λόγο; Κοινή χρήση δεδομένων χωρίς να μεταβιβάζονται στηρίγματα. Αποτελείται από τα ακόλουθα στοιχεία:
Δημιουργημένο πλαίσιο - δεδομένα
Πάροχος πλαισίου - παρέχει πλαίσιο σε όλα τα παιδιά
Περασμένη τιμή - δεδομένα που θέλετε να μοιραστείτε
Στο παιδί, πρέπει να εισαγάγετε το πλαίσιο και να καλέσετε το useContext Hook και να περάσετε το πλαίσιο ως όρισμα.
import { UserContext } from "./App",
const { name } = useContext(UserContext),
return <h1>Γεια σου {όνομα}<>
```
Ορίστε. Φαίνεται ωραίο. Κυρίως για τη μετάδοση παγκόσμιων δεδομένων όπως θέματα κ.λπ. Δεν συνιστάται για χρήση σε εργασίες με πολύ δυναμικές αλλαγές.
Φυσικά, μπορούμε να δημιουργήσουμε έναν προσαρμοσμένο πάροχο περιβάλλοντος και ένα προσαρμοσμένο Hook για να μειώσουμε το boilerplate... αλλά θα ασχοληθώ με τα προσαρμοσμένα Hooks στο επόμενο άρθρο.
useReducer
Μας επιτρέπει να διαχειριζόμαστε την κατάσταση και να επανεκτελούμε όταν η κατάσταση αλλάζει - όπως το useState. Είναι παρόμοιο με το redux reducer. Αυτό είναι καλύτερο από το useState όταν η λογική της κατάστασης είναι πιο περίπλοκη.
Για ποιο λόγο; Μπορεί να χρησιμοποιηθεί όταν θέλουμε να επαναφέρουμε την κατάσταση στην αρχική της τιμή. Παρακάτω μπορείτε να βρείτε ένα ωραίο παράδειγμα:
Πότε να το χρησιμοποιήσετε; Όταν θέλουμε να επιτύχουμε αναφορική ισότητα (μειώνοντας έτσι τον αριθμό των συναρτήσεων που δημιουργούνται). Αυτό το Hook επιστρέφει τη συνάρτηση, σε αντίθεση με το useMemo που επιστρέφει την τιμή.
Παράδειγμα: Δημιουργήστε μια συνάρτηση στο γονικό στοιχείο και στη συνέχεια περάστε την μέσω props
Θα καταγράφει στην κονσόλα σε κάθε απόδοση! Ακόμα και αν οι τιμές μέσα στο getSquaredValue() η λειτουργία δεν άλλαξε. Μπορούμε όμως να το αποφύγουμε αυτό τυλίγοντας αυτή τη συνάρτηση σε useCallback
Δεν είναι ουδέτερη όταν εξετάζουμε το κόστος των πόρων - το useMemo πρέπει να καλείται σε κάθε απόδοση, αποθηκεύει την τιμή στη μνήμη και συγκρίνει (επιβάρυνση μνήμης),
χρησιμοποιεί Memoization - την τεχνική βελτιστοποίησης, ειδική μορφή προσωρινής αποθήκευσης.
Θα πρέπει να το χρησιμοποιείτε μόνο σε 2 περιπτώσεις:
Αν θέλετε να αποφύγετε την κλήση ενός πολύπλοκου κώδικα σε κάθε απόδοση,
Αν θέλετε να επιτύχετε αναφορική ισότητα.
Ας δούμε λίγο πιο προσεκτικά τη δεύτερη περίπτωση. Θέλουμε να χρησιμοποιήσουμε το useEffect με ένα αντικείμενο ως εξάρτηση. Επειδή τα αντικείμενα συγκρίνονται με την αναφορά τους, η useEffect θα κληθεί σε κάθε απόδοση. Για να αποφύγουμε τέτοια πράγματα, μπορούμε να συνδυάσουμε το useEffect με το useMemo για να απομνημονεύσουμε τέτοια αντικείμενα και στη συνέχεια να περάσουμε αυτά τα απομνημονευμένα αντικείμενα στον πίνακα εξαρτήσεων. Σύντομο παράδειγμα:
Το αντικείμενο 'hobbit' κάνει τις εξαρτήσεις του useEffect Hook (γραμμή 49) να αλλάζουν σε κάθε απόδοση. Μετακινήστε το μέσα στο useEffect callback. Εναλλακτικά, τυλίξτε την αρχικοποίηση του 'hobbit' στο δικό του useMemo () Hook.eslintreact-hooks/exhaustive-deps
Το πιο σημαντικό πράγμα: το useRef δεν ενεργοποιεί την επανεκτέλεση (όπως το useState) επειδή δεν συνδέεται με τον κύκλο απόδοσης - διατηρεί την ίδια αναφορά μεταξύ των αποδόσεων.
const ref = useRef(0),
Για να καλέσετε την αποθηκευμένη τιμή, πρέπει να χρησιμοποιήσετε μια τρέχουσα ιδιότητα (η ref είναι ένα αντικείμενο) - ref.current
Η δεύτερη περίπτωση για την οποία μπορούμε να χρησιμοποιήσουμε αυτό το Hook είναι για να αναφερθούμε σε στοιχεία μέσα στην HTML. Κάθε στοιχείο έχει ένα χαρακτηριστικό ref. Έτσι, μπορούμε να χειριστούμε την εστίαση, τα γεγονότα κ.λπ.
Η τρίτη περίπτωση είναι ότι μπορούμε να χρησιμοποιήσουμε refs για να χειριστούμε μη ελεγχόμενα στοιχεία. Μπορείτε να διαβάσετε περισσότερα για αυτά στο έγγραφα αντιδράσεων, αλλά εν ολίγοις, μοιάζει ως εξής:
Όπως μπορείτε να δείτε, δεν υπάρχει κανένας χειριστής συμβάντος, απλά θυμάται την πληκτρολογημένη τιμή. Είναι ιδανικό για το χειρισμό βασικών φορμών, όταν απλά θέλετε να διαβάζετε τις αποθηκευμένες τιμές όταν τις χρειάζεστε (όπως κατά την υποβολή).
Μπόνους: Είναι ιδανικό όταν πρέπει να θυμάστε προηγούμενες τιμές κατάστασης. Μπορείτε να χρησιμοποιήσετε γι' αυτό το useEffect Hook, απλά περάστε την κατάσταση στο ref.
Όπως μπορείτε να δείτε, τα άγκιστρα δεν είναι τόσο προφανή. Μπορούμε να τα συνδυάσουμε για να λύσουμε πολλά προβλήματα. Σίγουρα θα ωφεληθείτε πολύ από τη μελέτη αυτού του θέματος.
Και υπάρχουν επίσης προσαρμοσμένα άγκιστρα...
Εν κατακλείδι, Άγκιστρα React έχουν φέρει επανάσταση στον τρόπο React προγραμματιστές προσέγγιση του κτιρίου διαδικτυακές εφαρμογές . Παρέχοντας έναν πιο διαισθητικό και αποτελεσματικό τρόπο διαχείρισης της κατάστασης και του κύκλου ζωής σε λειτουργικά στοιχεία, τα άγκιστρα έχουν γίνει αναπόσπαστο μέρος της Ανάπτυξη React .
Είτε είστε έμπειρος προγραμματιστής είτε ξεκινάτε μόλις με το React, η κατανόηση των πιο δημοφιλών hooks και των περιπτώσεων χρήσης τους είναι ζωτικής σημασίας. Με άγκιστρα όπως τα useState, useEffect, useContext και άλλα, Εξαρτήματα React μπορούν να κατασκευαστούν με καθαρότερο και πιο επαναχρησιμοποιήσιμο κώδικα. Επιπλέον, η δυνατότητα δημιουργίας προσαρμοσμένα άγκιστρα επιτρέπει στους προγραμματιστές να ενθυλακώσουν και να μοιραστούν τη λογική σε πολλαπλά στοιχεία, προωθώντας την επαναχρησιμοποίηση του κώδικα και την αρθρωτότητα. Καθώς το React συνεχίζει να εξελίσσεται και να εισάγει νέα χαρακτηριστικά, τα άγκιστρα θα διαδραματίσουν αναμφίβολα κεντρικό ρόλο στην αξιοποίηση όλων των δυνατοτήτων του πλαισίου.
Έτσι, είτε εργάζεστε σε μια μικρή εφαρμογή λειτουργίας είτε σε μια μεγάλης κλίμακας εφαρμογή ιστού, η υιοθέτηση της Άγκιστρα React θα βελτιώσει τη ροή εργασιών σας και θα ξεκλειδώσει μια πληθώρα δυνατοτήτων για τη δημιουργία ισχυρών και πλούσιων σε χαρακτηριστικά Εφαρμογές React .