Ορατότητα εξαρτημάτων στο React με απόδοση υπό όρους και φρουρούς
Bartlomiej Kuczynski
Μάθετε πώς να απλοποιείτε και να βελτιώνετε την ορατότητα των εξαρτημάτων στο React χρησιμοποιώντας την απόδοση υπό όρους και τα προστατευτικά εξαρτήματα.
Σήμερα θα ήθελα να συζητήσω πώς να ελέγχετε ορατότητα εξαρτημάτων στο React. Αλλά πριν ξεκινήσουμε υπάρχει ένα μικρό αποποίηση ευθυνών:
Η παρούσα λύση δεν είναι πολύ ασφαλής όσον αφορά την προστασία της εφαρμογής σας από "χάκερ" (όποιοι κι αν είναι αυτοί).
Να θυμάστε ότι πρέπει να προστατεύετε τα τελικά σας σημεία και να χρησιμοποιείτε καλές πρακτικές στη σχεδίαση εφαρμογών. Αυτή η λύση μόνο
κάνει τη δουλειά σας λίγο πιο εύκολη.
Εργασία, λύση προβλήματος ή τι θα θέλαμε να επιτύχουμε
Μια από τις πιο συνηθισμένες λειτουργίες είναι η εμφάνιση στοιχείων μόνο για μια ομάδα χρηστών που έχουν κάποια συγκεκριμένα δικαιώματα, ρόλους ή προνόμια. Κοινή λύση είναι να προσθέσετε κάποια εάνγια να κωδικός, ελέγξτε χειροκίνητα τις συνθήκες και εμφανίστε ή όχι στοιχεία.
Ας ρίξουμε μια ματιά στο SimpleAppHeader στοιχείο που περιέχει λίγα στοιχεία πλοήγησης.
<!-- wp:paragraph -->
<p><code>typescript jsx<br>
export const SimpleAppHeader: FC = () => {<br>
return (<br>
<header><br>
<nav><br>
<ul><br>
{<br>
currentUser.role === "ADMIN" &&<br>
<li>Στοιχείο μόνο για διαχειριστές</li><br>
}<br>
{<br>
currentUser.role === "USER" &&<br>
<li>User only component</li><br>
}<br>
{<br>
(currentUser.role === "ADMIN" || currentUser.role === "USER") &&<br>
<li>Στοιχείο μόνο για διαχειριστές και χρήστες</li><br>
}<br>
<li>Στοιχείο γενικής χρήσης</li><br>
</ul><br>
</nav><br>
</header><br>
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
Φαίνεται "όχι κακό". Πιθανώς θα μπορούσατε να ενθυλακώσετε τους ελέγχους σε συναρτήσεις για να μειώσετε την πολυπλοκότητα. currentUser είναι κάποιο αντικείμενο που έχει ρόλος αλλά θα μπορούσε να είναι οτιδήποτε θα θέλαμε να χρησιμοποιήσουμε ως αντικείμενο του μηχανισμού ελέγχου μας.
Αυτός ο κώδικας έχει κάποια προβλήματα. Εάν έργο μεγαλώνει πιθανώς χρησιμοποιούμε αυτές τις κατασκευές σε πολλά μέρη. Έτσι πρέπει να αντιγράψουμε, με κάποιο τρόπο, συνθήκες. Αυτός ο κώδικας είναι δύσκολο να διατηρηθεί και να αλλάξει στο μέλλον. Ειδικά όταν οι κανόνες πρόσβασης αλλάζουν κατά τη διάρκεια π.χ. πρέπει να αλλάξουμε currentUser σε κάτι άλλο. Είναι πολύ δύσκολο να δοκιμαστεί. Πρέπει να γράψετε πολλές δοκιμές απλά για να επαληθεύσετε αν η κατάστασή σας είναι εντάξει.
Απλούστευση
Ώρα να απλοποιήσουμε λίγο αυτόν τον κώδικα. Θα μπορούσαμε να αφαιρέσουμε μερικές μεθόδους και να κάνουμε τον κώδικα πιο σύντομο και λιγότερο περίπλοκο:
const isAdmin = (role: string): boolean => role === "ADMIN",
const isUser = (role: string): boolean => role === "USER",
const isAdminOrUser = (role: string): boolean => isAdmin(role) || isUser(role),
export const SimplifiedAppHeader: FC = () => { {
return (
{
isAdmin(currentUser.role) &&
Στοιχείο μόνο για διαχειριστές
}
{
isUser(currentUser.role) &&
Συστατικό μόνο για χρήστες
}
{
(isAdminOrUser(currentUser.role)) &&
Συστατικό μόνο για διαχειριστές και χρήστες
}
Στοιχείο γενικής χρήσης
)
}
```
Φαίνεται πολλά υποσχόμενο. Μειώνουμε το θόρυβο od γραμμές επανάληψης. Ο κώδικας είναι πιο ευανάγνωστος και ευκολότερα συντηρήσιμος. Αλλά ρίξτε μια ματιά στο συνάρτηση isAdminOrUser. Έχουμε μόνο δύο ρόλους. Αν εισάγουμε τρίτο ρόλο πρέπει να δημιουργήσουμε ένα άλλο σύνολο συναρτήσεων που συνδυάζει ρόλους. Ώρα για μια άλλη έκδοση.
Φιλτράρισμα
Ας εισαγάγουμε τη συνάρτηση hasRole που θα αντικαταστήσει το isX λειτουργίες.
const hasRole = (role: string, requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => role === s),
return found !== undefined,
}
export const FilteringAppHeader: FC = () => { {
return (
{
hasRole(currentUser.role, ["ADMIN"]) &&
Στοιχείο μόνο για διαχειριστές
}
{
hasRole(currentUser.role, ["USER"]) &&
Συστατικό μόνο για χρήστες
}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&
Συστατικό μόνο για διαχειριστές και χρήστες
}
Στοιχείο γενικής χρήσης
)
}
```
Τώρα φαίνεται καλό. Εξακολουθούμε να έχουμε συνθήκες στο html μέρος του κώδικα, αλλά τώρα μπορούμε να δοκιμάσουμε hasRole λειτουργία και να εμπιστεύεστε ότι θα χρησιμοποιηθεί με το σωστό σύνολο παραμέτρων. Η προσθήκη ή η αλλαγή ρόλων είναι πλέον ευκολότερη. Χρησιμοποιούμε πίνακα που περιέχει όλους τους τους ρόλους που χρειαζόμαστε στη θέση τους.
Ωστόσο, η λύση αυτή δεσμεύει currentUser αντικείμενο. Η υπόθεση είναι ότι το αντικείμενο είναι κατά κάποιο τρόπο παγκόσμιο ή εύκολα προσβάσιμο σε οποιαδήποτε θέση στην εφαρμογή. Μπορούμε λοιπόν να προσπαθήσουμε να το ενθυλακώσουμε αυτό. Φυσικά, μπορούμε να το μετακινήσουμε στο hasRole λειτουργία:
Το συστατικό χρειάζεται δύο ιδιότητες. Πρώτη παιδιά υπεύθυνος για το περιεχόμενο που φυλάσσεται. Δεύτερο requiredRoles που χειρίζεται σειρά ρόλων που μας δίνει πρόσβαση.
Χρειαζόμαστε αναπαράσταση αυτής της δομής. Είναι πολύ απλή. Επεκτείνουμε τον τύπο React.PropsWithChildren που έχει παιδιά ιδιοκτησία. Φυσικά μπορείτε να προσθέσετε χειροκίνητα την ιδιότητα αυτή στον τύπο σας και να παραλείψετε την επέκταση.
Και θα μπορούσα να πω να σταματήσετε εδώ, αλλά θα ήταν πολύ εύκολο. Τώρα έχουμε το συστατικό, και μπορούμε να το χρησιμοποιήσουμε στα άκρα 😀
`GuardService`
Το πρώτο βήμα θα είναι η εξωτερίκευση των ελέγχων. currentUser είναι σκληρά κωδικοποιημένη τιμή. Θα ήθελα να ενθυλακώσω αυτή την τιμή σε κάποια υπηρεσία, η οποία θα "ξέρει" πώς να επαληθεύει τους ρόλους. Τεχνικά αυτό σημαίνει ότι μετακινούμε hasRole συνάρτηση σε μια άλλη κατηγορία.
Δημιουργώ απλή διεπαφή IGuardService που έχει μόνο μία ιδιότητα - hasRole.
Πολύ καλύτερα. GuardService μας διαχωρίζουν από τη λογική που ελέγχει τους ρόλους. Μπορούμε να το αλλάξουμε χωρίς συνέπειες για την συστατικό. Συνήθης περίπτωση χρήσης είναι η χρήση προσομοίωσης σε δοκιμές και κάποια "πραγματική" υλοποίηση στον κώδικα παραγωγής.
Απαγορευμένο στοιχείο
Επόμενη βελτίωση θα είναι ο χειρισμός προσαρμοσμένων Απαγορευμένο στοιχείο. Η τρέχουσα λύση εμφανίζει κενό στοιχείο. Πρώτα πρέπει να αλλαγή IGuardProps προσθέτοντας τη συνάρτηση που θα αποδοθεί σε αυτό το στοιχείο.
Αυτή είναι προαιρετική ιδιότητα, το όνομα τελειώνει με χαρακτήρα ερωτηματικού. Έτσι θα μπορούσε να είναι συνάρτηση ή undefined. Πρέπει να να το χειριστεί σε Φρουρός συστατικό.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
return ,
} else {
return (
{props.forbidden()}
);
}
}
// ...
export const AppHeader: FC = () => { {
const guardService = new SimpleGuard(),
return (
Συστατικό μόνο για διαχειριστές
User only component
Απαγορευμένο - μόνο ο συντονιστής μπορεί να το δει αυτό
}>
Συστατικό συντονιστή
Στοιχείο διαχειριστή και χρήστη
Στοιχείο γενικής χρήσης
);
}
```
Ευελιξία των τύπων
Ώρα για τελευταία μεγάλη αλλαγή. Η τρέχουσα έκδοση του συστατικού υποστηρίζει μόνο ρόλους ως string. Θα μπορούσαμε να έχουμε πολλές διαφορετικές τύπους ακινήτων που θα θέλαμε να ελέγξουμε. Οι αριθμοί ή οι προσαρμοσμένοι τύποι δεν είναι τίποτα το ιδιαίτερο. Θα προσθέσω υποστήριξη γενικής χρήσης.
Το πρώτο βήμα είναι οι αλλαγές στο IGuardService διεπαφή. Οι υλοποιήσεις θα εξαρτώνται από τον τύπο της εξεταζόμενης τιμής. Θα μπορούσε να να είναι οτιδήποτε, οπότε η διεπαφή θα πρέπει να το χειριστεί.
export const AppHeader: FC = () => { {
const guardService = new SimpleGuard(),
const roleService = new RoleGuardService(),
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{name: "ADMIN"}]} guardService={roleService}>
<li>Στοιχείο μόνο για διαχειριστές</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Συστατικό μόνο για τον χρήστη</li>
</Guard>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Απαγορευμένο - μόνο ο συντονιστής μπορεί να το δει αυτό</div>}>
<li>Συστατικό συντονιστή</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<li>Στοιχείο διαχειριστή και χρήστη</li>
</Guard>
<li>Στοιχείο γενικής χρήσης</li>
</ul>
</nav>
</header>
);
}
Σε αυτό το παράδειγμα χρησιμοποιώ και τις δύο υλοποιήσεις του IGuardservice μόνο για ενδεικτικούς σκοπούς. Σε πραγματικές περιπτώσεις χρήσης πιθανότατα θα χρησιμοποιήσετε μόνο ένα.
Εξειδικευμένα εξαρτήματα και φωλιασμός
Το Φρουρός θα μπορούσε να φωλιάσει. Απλά να θυμάστε ότι η πρόσβαση θα επιλύεται με τη σειρά από την πιο εξωτερική περίπτωση.
export const NestAppHeader: FC = () => { {
const guardService = new SimpleGuard(),
return (
<header>
<nav>
<ul>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<Guard<string> requiredRoles={["ADMIN"]} guardService={guardService}>
<li>Στοιχείο μόνο για διαχειριστές</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Συστατικό μόνο για τον χρήστη</li>
</Guard>
<li>Στοιχείο διαχειριστή και χρήστη</li>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Απαγορευμένο - μόνο ο συντονιστής μπορεί να το δει αυτό</div>}>
<li>Συστατικό συντονιστή</li>
</Guard>
</Guard>
<li>Στοιχείο γενικής χρήσης</li>
</ul>
</nav>
</header>
);
}
Στο παραπάνω παράδειγμα Συστατικό συντονιστή δεν θα μπορούσε ποτέ να εμφανιστεί, επειδή ο χρήστης μπορεί να χειριστεί μόνο έναν ρόλο. Πρώτο Φρουρός όρια ρόλους σε ADMIN και ΧΡΗΣΤΗΣ, οπότε ΣΥΝΤΟΝΙΣΤΗΣ δεν θα περάσει ποτέ τον πρώτο έλεγχο.
Μπορούμε να κατασκευάσουμε εξειδικευμένα στοιχεία που αποκρύπτουν ορισμένες ιδιότητες.
export const AdminGuard = (props: Omit) => {
return
{props.children}
}
//...
export const SpecializedAppHeader: FC = () => { {
const guardService = new SimpleGuard(),
return (
Στοιχείο μόνο για διαχειριστές
Συστατικό μόνο για χρήστες
Forbidden - μόνο ο συντονιστής μπορεί να το δει αυτό
}>
Συστατικό συντονιστή
Συστατικό διαχειριστή και χρήστη
Στοιχείο γενικής χρήσης
);
}
```
Σε αυτή την περίπτωση AdminGuard προκαθορίστε το ADMIN ρόλο. Στις συνέπειες, πρέπει να ορίσουμε ρητά τον τύπο του ΡΟΛΟΣ τύπος παράμετρος.
Συνοψίστε
Σε αυτό το άρθρο θα σας δείξω πώς να δημιουργήσετε και να χρησιμοποιήσετε Φρουρόςσυστατικό στο React. Ξεκινάμε από πολύπλοκο κώδικα που είναι δύσκολο να να διαβάζει και να συντηρεί. Το εξελίσσουμε σε πιο φιλική προς τον προγραμματιστή κατάσταση και εισάγουμε προσαρμοσμένο λειτουργικό στοιχείο. Στη συνέχεια επεκτείνετε το συστατικό προσθέτοντας επιπλέον λειτουργίες, αναδιαμορφώνετε την υπηρεσία εξαγωγής και τέλος προσθέτετε γενικούς τύπους.
Τέλος, έχουμε συστατικό που μπορεί να φωλιάσει είναι εύκολο να δοκιμαστεί και να διατηρηθεί.