我建议的方法有点冗长,但我发现它可以很好地扩展到复杂的应用程序中。当你想显示一个模态时,触发一个描述你想看到哪个模态的动作:
调度一个动作来显示模态
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 组件,可以分派动作,从 store 状态中读取一些东西,并且恰好是 modals。
例如,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>
可以在您的应用程序中接受的props,但我想您可能有多种模态(例如信息模态、确认模态等),以及它们的多种样式。
可访问性和隐藏点击外部或退出键
关于模态的最后一个重要部分是,通常我们希望在用户单击外部或按下 Escape 时隐藏它们。
我建议您不要自己实施,而不是就实施此问题给您建议。考虑到可访问性,很难做到正确。
相反,我建议您使用可访问的现成模态组件,例如react-modal
. 它是完全可定制的,你可以在里面放任何你想要的东西,但它正确地处理了可访问性,这样盲人仍然可以使用你的模式。
您甚至react-modal
可以自己包装<Modal>
接受特定于您的应用程序的props并生成子按钮或其他内容。一切都只是组件!
其他方法
有不止一种方法可以做到。
有些人不喜欢这种方法的冗长,而更喜欢有一个<Modal>
组件,他们可以使用称为“门户”的技术直接在其组件内部呈现。Portals 允许你在你的内部渲染一个组件,而实际上它会在 DOM 中的预定位置渲染,这对于模态来说非常方便。
事实上,react-modal
我之前链接到的已经在内部这样做了,所以从技术上讲,你甚至不需要从顶部渲染它。我仍然觉得将我想显示的模态与显示它的组件分离是很好的,但是您也可以react-modal
直接从组件中使用,并跳过我上面写的大部分内容。
我鼓励您考虑这两种方法,对它们进行试验,然后选择最适合您的应用程序和团队的方法。