我建议的方法有点冗长,但我发现它可以很好地扩展到复杂的应用程序中。当你想显示模式,触发一个动作说明它莫代尔你想看到的:
调度动作以显示模态
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(字符串当然可以是常量;为简单起见,我使用内联字符串。)
编写Reducer以管理模态
然后确保您有一个只接受这些值的减速器:
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
将进行更新以包括有关当前可见的模态窗口的信息。
编写根模态组件
在组件层次结构的根目录中,添加一个<ModalRoot>
连接到Redux存储的组件。它将收听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组件,可以调度动作,从存储状态读取某些内容,并且碰巧是modal。
例如,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>
,接受特定于您的应用程序的道具并生成子按钮或其他内容。全部都是组件!
其他方法
有多种方法可以做到这一点。
有些人不喜欢这种方法的冗长性,而是希望拥有一个<Modal>
组件,他们可以使用称为“门户”的技术在其组件内部进行渲染。门户使您可以在内部渲染组件,而实际上它可以在DOM中的预定位置进行渲染,这对于模式化非常方便。
实际上,react-modal
我以前链接的内容已经在内部完成了,因此从技术上讲,您甚至不需要从顶部进行渲染。我仍然觉得将要显示的模态与显示它的组件分离是很好的方法,但是您也可以react-modal
直接从您的组件中使用它,而跳过上面我写的大部分内容。
我鼓励您考虑两种方法,进行试验,然后选择最适合您的应用和团队的方法。