Visibilité des composants dans React avec rendu conditionnel et protections
Bartlomiej Kuczynski
Découvrez comment simplifier et améliorer la visibilité des composants dans React à l'aide du rendu conditionnel et des composants de garde.
Aujourd'hui, je voudrais parler de la manière de contrôler visibilité des composants en React. Mais avant de commencer, il y a une petite clause de non-responsabilité :
La solution présentée n'est pas très sûre en ce qui concerne la protection de votre application contre les "pirates" (quels qu'ils soient).
Rappelez-vous que vous devez protéger vos points d'extrémité et utiliser de bonnes pratiques dans la conception de l'application. Cette solution ne fait que
Cette solution ne fait que faciliter un peu votre travail.
Tâche, solution au problème ou objectif à atteindre
L'une des fonctionnalités les plus courantes consiste à n'afficher le composant que pour un groupe d'utilisateurs disposant de certains droits spécifiques, rôles ou privilèges. La solution la plus courante consiste à ajouter des sià codeL'application de l'article 5 de la loi sur la protection de l'environnement permet de vérifier manuellement les conditions et d'afficher ou non les éléments.
Jetons un coup d'œil à SimpleAppHeader qui contient peu d'éléments de navigation.
<!-- wp:paragraph -->
<p><code>typescript jsx<br>
export const SimpleAppHeader : FC = () => {<br>
return (<br>)
<br>
<nav><br>
<ul><br>
{<br>
currentUser.role === "ADMIN" &&<br><br>L'utilisateur actuel a un rôle d'administrateur.
<li>Composant réservé à l'administrateur</li><br>
}<br><br><br>L'utilisateur actuel a un rôle à jouer dans l'organisation de son travail.
{<br><br>L'utilisateur actuel
currentUser.role === "USER" &&<br> <li>Composant réservé aux utilisateurs</li><br>.
<li>Composant utilisateur uniquement</li><br>.
}<br><br><br>L'utilisateur actuel a un rôle à jouer dans la vie de l'entreprise.
{<br><br>L'utilisateur actuel est le seul à avoir un rôle à jouer dans le système.
(currentUser.role === "ADMIN" || currentUser.role === "USER") &&<br> <li>Admin et User only component</li><br>.
<li>Composant réservé aux administrateurs et aux utilisateurs</li><br>.
}<br><br>Les éléments d'utilisation générale<br><br>Les éléments d'utilisation générale
<li>Élément d'utilisation générale</li><br>
</ul><br>
</nav><br><br><br>Nav
</header><br><br>Les éléments d'utilisation générale
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
Cela semble "pas mal". Il serait probablement possible d'encapsuler les contrôles dans des fonctions pour réduire la complexité. utilisateur actuel est un objet quelconque qui a rôle mais il peut s'agir de tout ce que nous souhaitons utiliser comme objet de notre mécanisme de contrôle.
Ce code pose quelques problèmes. Si le code projet grandit, nous utilisons probablement ces constructions dans de nombreux endroits. Nous devons donc les copier, d'une manière ou d'une autre, des conditions. Ce code est difficile à maintenir et à modifier à l'avenir. En particulier lorsque les règles d'accès changent pendant temps p.ex. nous devons changer utilisateur actuel en quelque chose d'autre. Il est très difficile de le tester. Il faut écrire de nombreux tests juste pour vérifier si votre état est correct.
Simplification
Il est temps de simplifier un peu ce code. Nous pourrions extraire certaines méthodes et rendre le code plus court et moins complexe :
Cela semble prometteur. Nous réduisons le bruit et les lignes répétitives. Le code est plus lisible et plus facile à maintenir. Mais jetez un coup d'œil à fonction isAdminOrUser. Nous n'avons que deux rôles. Si nous introduisons un troisième rôle, nous devons créer un autre ensemble de fonctions qui combine les rôles. Il est temps de passer à une autre version.
Filtrage
Introduisons la fonction aRôle qui remplacera notre isX fonctions.
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"]) &&
Composant réservé aux administrateurs
}
{
hasRole(currentUser.role, ["USER"]) &&
Composant utilisateur uniquement
}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&
Composant réservé aux administrateurs et aux utilisateurs
}
Élément d'utilisation générale
)
}
```
Le résultat est maintenant satisfaisant. Nous avons toujours des conditions dans la partie html du code, mais maintenant nous pouvons tester aRôle et de la confiance qu'elle suscite. sera utilisé avec les bons paramètres. Il est désormais plus facile d'ajouter ou de modifier des rôles. Nous utilisons un tableau qui contient tous les que nous devons mettre en place.
Toutefois, cette solution est contraignante pour utilisateur actuel l'objet. L'hypothèse est que l'objet est en quelque sorte global ou facile d'accès en n'importe où dans l'application. Nous pouvons donc essayer de l'encapsuler. Bien sûr, nous pouvons le déplacer vers aRôle fonction :
const hasRole = (requiredRole : string[]) : boolean => {
let found : string | undefined = requiredRole.find(s => currentUser.role === s) ;
return found !== undefined ;
}
Mais cela ne nous donne presque rien.
Les Garde Composant
Il est temps de créer un composant qui encapsule toute la logique. Je l'ai nommé Garde et voici comment je veux l'utiliser.
Le composant a besoin de deux propriétés. La première enfants responsable du contenu gardé. Deuxième rôles requis que Le système de gestion des rôles qui nous permet d'accéder à l'information.
Nous avons besoin d'une représentation de cette structure. C'est très simple. Nous étendons le type React.PropsWithChildren qui a enfants propriété. Bien entendu, vous pouvez ajouter manuellement cette propriété à votre type et omettre l'extension.
Et je pourrais dire stop ici, mais ce serait trop facile. Nous avons maintenant un composant, et nous pouvons l'utiliser à l'extrême 😀
`GuardService`
La première étape sera l'externalisation des contrôles. utilisateur actuel est une valeur codée en dur. Je voudrais encapsuler cette valeur dans un service qui "saura" comment vérifier les rôles. Techniquement, cela signifie que nous déplaçons aRôle à une autre fonction classe.
Je crée une interface simple IGuardService qui n'a qu'une seule propriété - aRôle.
Beaucoup mieux. Service de garde nous séparent de la logique qui vérifie les rôles. Nous pouvons la modifier sans conséquences pour notre composant. Le cas d'utilisation le plus courant est l'utilisation d'un simulacre dans les tests et d'une implémentation "réelle" dans le code de production.
Élément interdit
La prochaine amélioration portera sur la gestion des commandes personnalisées. Interdit élément. La solution actuelle rend l'élément vide. Nous devons d'abord changer IGuardProps l'ajout d'une fonction qui sera rendue à cet élément.
Il s'agit d'une propriété facultative, dont le nom se termine par un point d'interrogation. Il peut donc s'agir d'une fonction ou d'une indéfini. Nous devons le traiter en Garde de la composante.
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 (
Composant réservé aux administrateurs
Composant réservé à l'utilisateur
Interdit - seul le modérateur peut le voir
}>
Composant modérateur
Composant administrateur et utilisateur
Élément d'utilisation générale
) ;
}
```
Flexibilité des types
Il est temps de procéder au dernier grand changement. La version actuelle du composant ne prend en charge que les rôles de chaîne de caractères. Nous pourrions avoir plusieurs les types de propriétés que nous souhaitons vérifier. Les nombres ou les types personnalisés n'ont rien de spécial. Je vais ajouter le support des génériques.
La première étape consiste à modifier les IGuardService interface. Les implémentations dépendront du type de valeur testée. Elle pourrait n'importe quoi, l'interface doit donc le gérer.
export const AppHeader : FC = () => {
const guardService = new SimpleGuard() ;
const roleService = new RoleGuardService() ;
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{nom : "ADMIN"}]} guardService={roleService}>
<li>Composant réservé aux administrateurs</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Composant réservé à l'utilisateur</li>
</Guard>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Interdit - seul le modérateur peut voir ceci</div>}>
<li>Composant du modérateur</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<li>Composant Admin et Utilisateur</li>
</Guard>
<li>Élément d'utilisation générale</li>
</ul>
</nav>
</header>
);
}
Dans cet exemple, j'utilise les deux implémentations de IGuardservice à des fins d'illustration. Dans les cas d'utilisation réels, vous n'en utiliseront probablement qu'un seul.
Composants spécialisés et emboîtement
Les Garde peuvent être imbriqués. Rappelez-vous simplement que l'accès sera résolu dans l'ordre de l'instance la plus externe.
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>Composant réservé aux administrateurs</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Composant réservé à l'utilisateur</li>
</Guard>
<li>Composant Admin et Utilisateur</li>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Interdit - seul le modérateur peut voir ceci</div>}>
<li>Composant du modérateur</li>
</Guard>
</Guard>
<li>Élément d'utilisation générale</li>
</ul>
</nav>
</header>
);
}
Dans l'exemple ci-dessus Composant du modérateur ne peut jamais apparaître, car l'utilisateur ne peut assumer qu'un seul rôle. Première Garde limites rôles à ADMIN et UTILISATEURAinsi MODÉRATEUR ne passera jamais le premier contrôle.
Nous pouvons construire des composants spécialisés qui masquent certaines propriétés.
export const AdminGuard = (props : Omit) => {
return
{props.children}
}
//...
export const SpecializedAppHeader : FC = () => {
const guardService = new SimpleGuard() ;
return (
Composant réservé aux administrateurs
Composant réservé aux utilisateurs
Interdit - seul le modérateur peut voir ceci
}>
Composant modérateur
Composant Admin et Utilisateur
Élément d'utilisation générale
) ;
}
```
Dans ce cas AdminGuard prédéfinir ADMIN rôle. En conséquence, nous devons définir explicitement le type de RÔLE type paramètre.
Résumé
Dans cet article, je vous montre comment créer et utiliser des Gardecomposant en React. Nous partons d'un code complexe difficile à lire et à maintenir. Nous le faisons évoluer vers un état plus convivial pour les développeurs et introduisons des composants fonctionnels personnalisés. Ensuite, nous étendre le composant en ajoutant des fonctionnalités supplémentaires, refactoriser le service d'extraction et enfin ajouter des types génériques.
Enfin, nous disposons d'un composant qui peut être imbriqué et qui est facile à tester et à maintenir.