Podejście, które sugeruję, jest nieco szczegółowe, ale udało mi się dość dobrze skalować w złożone aplikacje. Jeśli chcesz pokazać modal, uruchom akcję opisującą, który modal chcesz zobaczyć:
Wywołanie akcji w celu wyświetlenia modalu
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(Ciągi mogą być oczywiście stałymi; używam ciągów wbudowanych dla uproszczenia.)
Pisanie reduktora w celu zarządzania stanem modalnym
Następnie upewnij się, że masz reduktor, który akceptuje tylko te wartości:
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 */
})
Wspaniały! Teraz, gdy wydasz akcję, state.modal
zaktualizuje się, aby uwzględnić informacje o aktualnie widocznym oknie modalnym.
Pisanie głównego elementu modalnego
W katalogu głównym hierarchii komponentów dodaj <ModalRoot>
komponent, który jest podłączony do sklepu Redux. Będzie nasłuchiwał state.modal
i wyświetlał odpowiedni komponent modalny, przekazując rekwizyty z 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)
Co my tu zrobiliśmy? ModalRoot
odczytuje prąd modalType
i modalProps
od state.modal
którego jest podłączony, i renderuje odpowiedni komponent, taki jak DeletePostModal
lub ConfirmLogoutModal
. Każdy modal jest elementem!
Pisanie określonych składników modalnych
Nie ma tu ogólnych zasad. Są tylko komponentami React, które mogą wywoływać akcje, czytać coś ze stanu sklepu i po prostu być modalne .
Na przykład, DeletePostModal
może wyglądać następująco:
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)
DeletePostModal
Jest podłączony do sklepu, więc może wyświetlić tytuł posta i działa jak dowolnego podłączonego urządzenia: może wysyłką działań, w tym hideModal
przypadku konieczne jest, aby ukryć się.
Wyodrębnianie elementu prezentacji
Byłoby niewygodne kopiowanie i wklejanie tej samej logiki układu dla każdego „określonego” modalu. Ale masz komponenty, prawda? Możesz więc wyodrębnić prezentację <Modal>
komponent , który nie wie, co robią poszczególne modały, ale obsługuje ich wygląd.
Następnie określone moduły, takie jak DeletePostModal
można użyć do renderowania:
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)
To do ciebie należy wymyślenie zestawu rekwizytów <Modal>
można zaakceptować w twojej aplikacji, ale wyobrażam sobie, że możesz mieć kilka rodzajów modów (np. Modal informacji, modal potwierdzenia itp.) I kilka stylów dla nich.
Dostępność i ukrywanie na Click Outside lub Escape Key
Ostatnią ważną częścią modali jest to, że generalnie chcemy je ukryć, gdy użytkownik kliknie na zewnątrz lub naciśnie klawisz Escape.
Zamiast udzielać porad na temat wdrażania tego, sugeruję, abyś sam tego nie wdrażał. Trudno jest dobrze zrozumieć, biorąc pod uwagę dostępność.
Zamiast tego sugerowałbym, abyś użył dostępnego, modalnego komponentu, takiego jak react-modal
. Jest całkowicie konfigurowalny, możesz umieścić w nim wszystko, co chcesz, ale poprawnie obsługuje dostępność, dzięki czemu niewidomi mogą nadal korzystać z twojego modalu.
Możesz nawet owinąć react-modal
własny<Modal>
który akceptuje rekwizyty specyficzne dla twoich aplikacji i generuje przyciski potomne lub inne treści. To wszystko tylko komponenty!
Inne podejścia
Jest na to więcej niż jeden sposób.
Niektórzy ludzie nie lubią gadatliwości tego podejścia i wolą mieć <Modal>
komponent, który mogą renderować bezpośrednio w swoich komponentach za pomocą techniki zwanej „portalami”. Portale pozwalają ci renderować komponent wewnątrz twojego, gdy faktycznie będzie renderowany w określonym miejscu w DOM, co jest bardzo wygodne w przypadku modałów.
W rzeczywistości react-modal
, o czym już wcześniej wspomniałem, robi to wewnętrznie, więc technicznie nie musisz nawet renderować tego z góry. Nadal uważam, że miło jest oddzielić modal, który chcę pokazać, od pokazującego go komponentu, ale możesz także używać react-modal
bezpośrednio z komponentów i pomijać większość tego, co napisałem powyżej.
Zachęcam do rozważenia obu podejść, eksperymentowania z nimi i wybrania tego, co według ciebie będzie najlepsze dla Twojej aplikacji i zespołu.