Je crée une application qui doit afficher une boîte de dialogue de confirmation dans certaines situations.
Disons que je veux supprimer quelque chose, alors je vais envoyer une action comme deleteSomething(id)
ça, donc un réducteur attrapera cet événement et remplira le réducteur de dialogue afin de le montrer.
Mon doute vient quand cette boîte de dialogue se soumet.
- Comment ce composant peut-il envoyer l'action appropriée en fonction de la première action envoyée?
- Le créateur d'action doit-il gérer cette logique?
- Pouvons-nous ajouter des actions à l'intérieur du réducteur?
Éditer:
pour le rendre plus clair:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
J'essaie donc de réutiliser le composant de dialogue. Afficher / masquer la boîte de dialogue n'est pas le problème car cela peut être facilement fait dans le réducteur. Ce que j'essaie de spécifier, c'est comment répartir l'action du côté droit en fonction de l'action qui démarre le flux du côté gauche.
javascript
modal-dialog
redux
react-redux
carlesba
la source
la source
Réponses:
L'approche que je suggère est un peu verbeuse, mais je l'ai trouvée assez bien adaptée aux applications complexes. Lorsque vous voulez montrer un modal, le feu d' une action décrivant ce qui vous modal souhaitez voir:
Envoi d'une action pour afficher le modal
(Les chaînes peuvent être des constantes bien sûr; j'utilise des chaînes en ligne pour plus de simplicité.)
Écrire un réducteur pour gérer l'état modal
Assurez-vous ensuite que vous disposez d'un réducteur qui accepte simplement ces valeurs:
Génial! Désormais, lorsque vous envoyez une action,
state.modal
sera mise à jour pour inclure les informations sur la fenêtre modale actuellement visible.Écriture du composant racine modal
À la racine de la hiérarchie de vos composants, ajoutez un
<ModalRoot>
composant connecté au magasin Redux. Il écouterastate.modal
et affichera un composant modal approprié, transmettant les accessoires à partir dustate.modal.modalProps
.Qu'avons-nous fait ici?
ModalRoot
lit le courantmodalType
et àmodalProps
partirstate.modal
de laquelle il est relié, et rend un tel composant comme correspondantDeletePostModal
ouConfirmLogoutModal
. Chaque modal est un composant!Écriture de composants modaux spécifiques
Il n'y a pas de règles générales ici. Ce ne sont que des composants React qui peuvent envoyer des actions, lire quelque chose à partir de l'état du magasin et être des modaux .
Par exemple,
DeletePostModal
pourrait ressembler à:Le
DeletePostModal
est connecté au magasin afin qu'il puisse afficher le titre du message et fonctionne comme n'importe quel composant connecté: il peut envoyer des actions, y comprishideModal
lorsqu'il est nécessaire de se cacher.Extraire un composant de présentation
Il serait gênant de copier-coller la même logique de disposition pour chaque modal «spécifique». Mais vous avez des composants, non? Vous pouvez donc extraire un composant de présentation
<Modal>
qui ne sait pas ce que font les modaux particuliers, mais gère leur apparence.Ensuite, des modaux spécifiques tels que
DeletePostModal
peuvent l'utiliser pour le rendu:C'est à vous de trouver un ensemble d'accessoires qui
<Modal>
peuvent accepter dans votre application, mais j'imagine que vous pourriez avoir plusieurs types de modaux (par exemple, modal info, modal de confirmation, etc.), et plusieurs styles pour eux.Accessibilité et masquage du clic à l'extérieur ou de la touche d'échappement
La dernière partie importante concernant les modaux est que, généralement, nous voulons les masquer lorsque l'utilisateur clique à l'extérieur ou appuie sur Échap.
Au lieu de vous donner des conseils sur la mise en œuvre de cela, je vous suggère de ne pas le mettre en œuvre vous-même. Il est difficile de bien faire compte tenu de l'accessibilité.
Au lieu de cela, je vous suggère d'utiliser un composant modal disponible sur le marché tel que
react-modal
. Il est entièrement personnalisable, vous pouvez y mettre tout ce que vous voulez, mais il gère correctement l'accessibilité afin que les aveugles puissent toujours utiliser votre modal.Vous pouvez même envelopper
react-modal
le vôtre<Modal>
qui accepte les accessoires spécifiques à vos applications et génère des boutons enfants ou d'autres contenus. Ce ne sont que des composants!Autres approches
Il y a plus d'une façon de le faire.
Certaines personnes n'aiment pas la verbosité de cette approche et préfèrent avoir un
<Modal>
composant qu'elles peuvent rendre directement à l'intérieur de leurs composants avec une technique appelée «portails». Les portails vous permettent de restituer un composant à l'intérieur du vôtre alors qu'en fait il sera rendu à un endroit prédéterminé dans le DOM, ce qui est très pratique pour les modaux.En fait,
react-modal
je l'ai déjà fait précédemment en interne, donc techniquement, vous n'avez même pas besoin de le rendre par le haut. Je trouve toujours agréable de découpler le modal que je veux montrer du composant qui le montre, mais vous pouvez également l'utiliserreact-modal
directement à partir de vos composants et ignorer la plupart de ce que j'ai écrit ci-dessus.Je vous encourage à considérer les deux approches, à les expérimenter et à choisir ce qui vous convient le mieux pour votre application et pour votre équipe.
la source
modalProps
à l'action. Cela viole cependant la règle de maintien de l'état sérialisable. Comment puis-je surmonter ce problème?Mise à jour : React 16.0 a introduit des portails via un
ReactDOM.createPortal
lienMise à jour : les prochaines versions de React (Fibre: probablement 16 ou 17) incluront une méthode pour créer des portails:
ReactDOM.unstable_createPortal()
lienUtiliser des portails
Dan Abramov répond que la première partie est très bien, mais implique beaucoup de passe-partout. Comme il l'a dit, vous pouvez également utiliser des portails. Je vais développer un peu cette idée.
L'avantage d'un portail est que la fenêtre contextuelle et le bouton restent très proches de l'arborescence React, avec une communication parent / enfant très simple à l'aide d'accessoires: vous pouvez facilement gérer des actions asynchrones avec des portails, ou laisser le parent personnaliser le portail.
Qu'est-ce qu'un portail?
Un portail vous permet d'effectuer
document.body
un rendu directement à l'intérieur d' un élément profondément imbriqué dans votre arbre React.L'idée est que, par exemple, vous restituez dans le corps l'arborescence React suivante:
Et vous obtenez en sortie:
Le
inside-portal
nœud a été traduit à l'intérieur<body>
, au lieu de son emplacement normal et profondément imbriqué.Quand utiliser un portail
Un portail est particulièrement utile pour afficher les éléments qui devraient aller au-dessus de vos composants React existants: popups, listes déroulantes, suggestions, hotspots
Pourquoi utiliser un portail
Plus de problème de z-index : un portail vous permet de faire un rendu
<body>
. Si vous souhaitez afficher une popup ou une liste déroulante, c'est une très bonne idée si vous ne voulez pas avoir à vous battre contre des problèmes d'index z. Les éléments du portail sont ajoutésdocument.body
dans l'ordre de montage, ce qui signifie qu'à moins de jouer avecz-index
, le comportement par défaut sera d'empiler les portails les uns sur les autres, dans l'ordre de montage. En pratique, cela signifie que vous pouvez ouvrir une fenêtre contextuelle en toute sécurité à l'intérieur d'une autre fenêtre contextuelle et être sûr que la deuxième fenêtre contextuelle sera affichée en haut de la première, sans même avoir à y penserz-index
.En pratique
Le plus simple: utilisez l'état React local: si vous pensez que pour une simple fenêtre de confirmation de suppression, cela ne vaut pas la peine d'avoir le passe-partout Redux, alors vous pouvez utiliser un portail et cela simplifie considérablement votre code. Pour un tel cas d'utilisation, où l'interaction est très locale et est en fait un détail d'implémentation, vous souciez-vous vraiment du rechargement à chaud, du voyage dans le temps, de la journalisation des actions et de tous les avantages que Redux vous apporte? Personnellement, je n'utilise pas l'état local dans ce cas. Le code devient aussi simple que:
Simple: vous pouvez toujours utiliser l'état Redux : si vous le souhaitez vraiment, vous pouvez toujours utiliser
connect
pour choisir d'DeleteConfirmationPopup
afficher ou non le. Comme le portail reste profondément imbriqué dans votre arborescence React, il est très simple de personnaliser le comportement de ce portail car votre parent peut transmettre des accessoires au portail. Si vous n'utilisez pas de portails, vous devez généralement afficher vos fenêtres contextuelles en haut de votre arborescence React pourz-index
raisons, et doivent généralement penser à des choses comme "comment personnaliser le DeleteConfirmationPopup générique que j'ai construit en fonction du cas d'utilisation". Et généralement, vous trouverez des solutions assez hacky à ce problème, comme l'envoi d'une action qui contient des actions de confirmation / annulation imbriquées, une clé de bundle de traduction, ou pire encore, une fonction de rendu (ou autre chose non sérialisable). Vous n'êtes pas obligé de le faire avec des portails, et vous pouvez simplement passer des accessoires réguliers, car ceDeleteConfirmationPopup
n'est qu'un enfant duDeleteButton
Conclusion
Les portails sont très utiles pour simplifier votre code. Je ne pouvais plus m'en passer.
Notez que les implémentations de portail peuvent également vous aider avec d'autres fonctionnalités utiles telles que:
react-portal ou react-modal conviennent aux fenêtres contextuelles, aux modaux et aux superpositions qui doivent être en plein écran, généralement centrées au milieu de l'écran.
react-tether est inconnu de la plupart des développeurs React, mais c'est l'un des outils les plus utiles que vous puissiez trouver. Tether vous permet de créer des portails, mais positionnera automatiquement le portail par rapport à une cible donnée. C'est parfait pour les info-bulles, les listes déroulantes, les points chauds, les boîtes d'aide ... Si vous avez déjà eu un problème avec la position
absolute
/relative
etz-index
, ou votre liste déroulante en dehors de votre fenêtre, Tether résoudra tout cela pour vous.Vous pouvez, par exemple, implémenter facilement des hotspots d'intégration, qui se transforment en info-bulle une fois cliqué:
Un vrai code de production ici. Rien de plus simple :)
Edit : vient de découvrir la passerelle de réaction qui permet de rendre les portails dans le nœud de votre choix (pas forcément le corps)
Edit : il semble que react-popper puisse être une alternative décente à react-tether. PopperJS est une bibliothèque qui calcule uniquement une position appropriée pour un élément, sans toucher directement au DOM, permettant à l'utilisateur de choisir où et quand il veut placer le nœud DOM, tandis que Tether s'ajoute directement au corps.
Edit : il y a aussi react-slot-fill qui est intéressant et peut aider à résoudre des problèmes similaires en permettant de rendre un élément dans un emplacement d'élément réservé que vous placez où vous le souhaitez dans votre arborescence
la source
<Portal>
vient-il? Je suppose que c'est react-portal, mais ce serait bien de savoir avec certitude.Beaucoup de bonnes solutions et de précieux commentaires par des experts connus de la communauté JS sur le sujet peuvent être trouvés ici. Cela pourrait être un indicateur que ce n'est pas un problème aussi banal que cela puisse paraître. Je pense que c'est pourquoi cela pourrait être la source de doutes et d'incertitudes sur la question.
Le problème fondamental ici est que dans React, vous n'êtes autorisé à monter le composant que sur son parent, ce qui n'est pas toujours le comportement souhaité. Mais comment résoudre ce problème?
Je propose la solution, adressée pour résoudre ce problème. Une définition plus détaillée du problème, src et des exemples peuvent être trouvés ici: https://github.com/fckt/react-layer-stack#rationale
Jetez un œil à https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example pour voir l'exemple concret qui répond à votre question:
la source
À mon avis, la mise en œuvre minimale minimale a deux exigences. Un état qui garde la trace de l'ouverture ou non du modal, et un portail pour rendre le modal en dehors de l'arborescence de réaction standard.
Le composant ModalContainer ci-dessous implémente ces exigences ainsi que les fonctions de rendu correspondantes pour le modal et le déclencheur, qui est responsable de l'exécution du rappel pour ouvrir le modal.
Et voici un cas d'utilisation simple ...
J'utilise des fonctions de rendu, car je souhaite isoler la gestion de l'état et la logique standard de l'implémentation du composant modal et déclencheur rendu. Cela permet aux composants rendus d'être ce que vous voulez qu'ils soient. Dans votre cas, je suppose que le composant modal pourrait être un composant connecté qui reçoit une fonction de rappel qui envoie une action asynchrone.
Si vous devez envoyer des accessoires dynamiques au composant modal à partir du composant déclencheur, ce qui, espérons-le, ne se produit pas trop souvent, je recommande d'envelopper le ModalContainer avec un composant conteneur qui gère les accessoires dynamiques dans son propre état et améliore les méthodes de rendu d'origine comme alors.
la source
Enveloppez le modal dans un conteneur connecté et effectuez l'opération asynchrone ici. De cette façon, vous pouvez atteindre à la fois l'envoi pour déclencher des actions et l'accessoire onClose. Pour accéder
dispatch
aux accessoires, ne passez pas lamapDispatchToProps
fonction àconnect
.L'application où le modal est rendu et son état de visibilité est défini:
la source