L'approccio che suggerisco è un po 'prolisso, ma l'ho trovato per adattarsi abbastanza bene ad app complesse. Quando vuoi mostrare un modale, lancia un'azione che descriva quale modale ti piacerebbe vedere:
Invio di un'azione per mostrare il modale
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(Le stringhe possono essere costanti ovviamente; sto usando stringhe in linea per semplicità.)
Scrivere un riduttore per gestire lo stato modale
Quindi assicurati di avere un riduttore che accetta solo questi valori:
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 */
})
Grande! Ora, quando si invia un'azione, state.modal
verrà aggiornato per includere le informazioni sulla finestra modale attualmente visibile.
Scrittura del componente modale root
Alla radice della gerarchia dei componenti, aggiungi un <ModalRoot>
componente collegato all'archivio Redux. Ascolterà state.modal
e visualizzerà un componente modale appropriato, inoltrando gli oggetti di scena dal 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)
Cosa abbiamo fatto qui? ModalRoot
legge la corrente modalType
e modalProps
da state.modal
cui è connessa e rende un componente corrispondente come DeletePostModal
oConfirmLogoutModal
. Ogni modale è un componente!
Scrittura di componenti modali specifici
Non ci sono regole generali qui. Sono solo componenti di React che possono inviare azioni, leggere qualcosa dallo stato del punto vendita e semplicemente essere modali .
Ad esempio, DeletePostModal
potrebbe apparire come:
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)
Il DeletePostModal
è collegato al negozio in modo da poter visualizzare il titolo del post e funziona come qualsiasi componente collegato: si può inviare azioni, tra cui hideModal
quando è necessario nascondersi.
Estrazione di un componente di presentazione
Sarebbe scomodo copiare e incollare la stessa logica di layout per ogni modale "specifico". Ma hai dei componenti, giusto? Quindi puoi estrarre una presentazione <Modal>
componente di che non sa cosa fanno i particolari modali, ma gestisce il loro aspetto.
Quindi, modali specifici come DeletePostModal
possono usarlo per il rendering:
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)
Sta a te <Modal>
inventare una serie di oggetti di scena che possono accettare nella tua applicazione, ma immagino che potresti avere diversi tipi di modali (ad es. Informazioni modali, conferma modali, ecc.) E diversi stili per loro.
Accessibilità e nascondersi quando fai clic su Chiave esterna o Esc
L'ultima parte importante delle modali è che generalmente vogliamo nasconderle quando l'utente fa clic all'esterno o preme Esc.
Invece di darti consigli su come implementarlo, ti suggerisco di non implementarlo da solo. È difficile capire bene l'accessibilità.
Invece, ti suggerirei di utilizzare un componente modale standard accessibile come react-modal
. È completamente personalizzabile, puoi inserire tutto ciò che vuoi al suo interno, ma gestisce l'accessibilità in modo che i non vedenti possano ancora usare il tuo modale.
Puoi anche includere il react-modal
tuo <Modal>
che accetta oggetti di scena specifici per le tue applicazioni e genera pulsanti figlio o altri contenuti. Sono solo componenti!
Altri approcci
C'è più di un modo per farlo.
Ad alcune persone non piace la verbosità di questo approccio e preferiscono avere un <Modal>
componente che possono renderizzare direttamente all'interno dei loro componenti con una tecnica chiamata "portali". I portali ti consentono di eseguire il rendering di un componente all'interno del tuo mentre in realtà verrà eseguito il rendering in un punto predeterminato nel DOM, il che è molto conveniente per i modali.
In effetti, che react-modal
ho già collegato precedentemente, lo fa internamente, quindi tecnicamente non è nemmeno necessario renderlo dall'alto. Trovo ancora bello disaccoppiare il modale che voglio mostrare dal componente mostrandolo, ma puoi anche usare react-modal
direttamente dai tuoi componenti e saltare la maggior parte di ciò che ho scritto sopra.
Ti incoraggio a prendere in considerazione entrambi gli approcci, a sperimentarli e a scegliere ciò che ritieni più adatto alla tua app e al tuo team.