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
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(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:
const initialState = {
modalType: null,
modalProps: {}
}
function modal(state = initialState, action) {
switch (action.type) {
case 'SHOW_MODAL':
return {
modalType: action.modalType,
modalProps: action.modalProps
}
case 'HIDE_MODAL':
return initialState
default:
return state
}
}
/* .... */
const rootReducer = combineReducers({
modal,
/* other reducers */
})
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 écoutera state.modal
et affichera un composant modal approprié, transmettant les accessoires à partir du state.modal.modalProps
.
// These are regular React components we will write soon
import DeletePostModal from './DeletePostModal'
import ConfirmLogoutModal from './ConfirmLogoutModal'
const MODAL_COMPONENTS = {
'DELETE_POST': DeletePostModal,
'CONFIRM_LOGOUT': ConfirmLogoutModal,
/* other modals */
}
const ModalRoot = ({ modalType, modalProps }) => {
if (!modalType) {
return <span /> // after React v15 you can return null here
}
const SpecificModal = MODAL_COMPONENTS[modalType]
return <SpecificModal {...modalProps} />
}
export default connect(
state => state.modal
)(ModalRoot)
Qu'avons-nous fait ici? ModalRoot
lit le courant modalType
et à modalProps
partir state.modal
de laquelle il est relié, et rend un tel composant comme correspondant DeletePostModal
ou ConfirmLogoutModal
. 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 à:
import { deletePost, hideModal } from '../actions'
const DeletePostModal = ({ post, dispatch }) => (
<div>
<p>Delete post {post.name}?</p>
<button onClick={() => {
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
}}>
Yes
</button>
<button onClick={() => dispatch(hideModal())}>
Nope
</button>
</div>
)
export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)
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 compris hideModal
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:
import { deletePost, hideModal } from '../actions'
import Modal from './Modal'
const DeletePostModal = ({ post, dispatch }) => (
<Modal
dangerText={`Delete post ${post.name}?`}
onDangerClick={() =>
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
})
/>
)
export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)
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'utiliser react-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.