我建议的方法有点冗长,但我发现它可以很好地扩展到复杂的应用程序中。当您想要显示模式时,请触发一个描述您希望看到的模式的操作:
调度操作以显示模式
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(字符串当然可以是常量;为了简单起见,我使用内联字符串。
编写化简器来管理模态
然后,请确保您有一个只接受这些值的化简器:
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 */
})
伟大!现在,当您调度操作时,将更新以包含有关当前可见模式窗口的信息。state.modal
编写根模态组件
在组件层次结构的根目录中,添加连接到 Redux 存储的组件。它将侦听并显示适当的模式组件,从 .<ModalRoot>
state.modal
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)
我们在这里做了什么? 读取电流和它所连接的电流,并呈现相应的组件,如 或 。每个模态都是一个组件!ModalRoot
modalType
modalProps
state.modal
DeletePostModal
ConfirmLogoutModal
编写特定模态组件
这里没有一般规则。它们只是 React 组件,可以调度操作,从存储状态读取某些内容,并且恰好是模态。
例如,可能如下所示:DeletePostModal
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
hideModal
提取表示组件
为每个“特定”模式复制粘贴相同的布局逻辑会很尴尬。但是你有组件,对吧?因此,您可以提取一个表示组件,该组件不知道特定模式的作用,但处理它们的外观。<Modal>
然后,特定的模式(例如)可以使用它进行渲染:DeletePostModal
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)
由您来提出一组可以在您的应用程序中接受的道具,但我想您可能有几种模态(例如信息模态,确认模态等),以及它们的几种样式。<Modal>
点击外部或转义键时的可访问性和隐藏
关于模式的最后一个重要部分是,通常我们希望在用户单击外部或按Escape时隐藏它们。
我建议你不要给你关于实现它的建议,而是建议你不要自己实现它。考虑到可访问性,很难正确。
相反,我建议您使用可访问的现成模态组件,例如 react-modal
。它是完全可定制的,你可以把任何你想要的东西放在里面,但它正确地处理了可访问性,所以盲人仍然可以使用你的模态。
您甚至可以将自己包装在接受特定于您的应用程序的道具并生成子按钮或其他内容中。一切都只是组件!react-modal
<Modal>
其他方法
有多种方法可以做到这一点。
有些人不喜欢这种方法的冗长性,他们更喜欢有一个组件,他们可以使用一种称为“门户”的技术在其组件中呈现。门户允许您在组件内部渲染组件,而实际上它将在DOM中的预定位置进行渲染,这对于模式来说非常方便。<Modal>
事实上,我之前链接到的反应模态
已经在内部做到了这一点,所以从技术上讲,你甚至不需要从顶部渲染它。我仍然发现将我想显示的模态与显示它的组件分离是很好的,但是您也可以直接从组件中使用,并跳过我上面写的大部分内容。react-modal
我鼓励你考虑这两种方法,尝试它们,并选择你认为最适合你的应用和团队的方法。