问题描述
我正在构建一个需要在某些情况下显示确认对话框的应用。
I'm building an app that needs to show a confirm dialog in some situations.
假设我要删除某些内容,然后我会发送一个动作喜欢 deleteSomething(id)
所以一些reducer会捕获该事件并填充对话框reducer以显示它。
Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id)
so some reducer will catch that event and will fill the dialog reducer in order to show it.
这个对话框提交时我怀疑了。
My doubt comes when this dialog submits.
- 该组件如何根据调度的第一个动作调度正确的操作?
- 动作创建者应该处理这个逻辑吗?
- 我们可以在减速器中添加动作吗?
编辑:
使其更清晰:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
所以我正在尝试重用对话框组件。显示/隐藏对话框不是问题,因为这可以在reducer中轻松完成。我想要指定的是如何根据左侧开始流动的动作从右侧调度动作。
So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.
推荐答案
我建议的方法有点冗长,但我发现它可以很好地扩展到复杂的应用程序。当您想要显示模态时,请触发描述您希望看到哪个模式的操作:
The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(字符串当然可以是常量;为简单起见,我使用内联字符串。)
(Strings can be constants of course; I’m using inline strings for simplicity.)
然后确保你有一个只接受这些值的reducer:
Then make sure you have a reducer that just accepts these values:
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
将更新以包含有关当前可见模态窗口的信息。
Great! Now, when you dispatch an action, state.modal
will update to include the information about the currently visible modal window.
在组件层次结构的根目录下,添加< ModalRoot>
组件连接到Redux商店的。它将侦听 state.modal
并显示一个合适的模态组件,从 state.modal.modalProps转发道具
。
At the root of your component hierarchy, add a <ModalRoot>
component that is connected to the Redux store. It will listen to state.modal
and display an appropriate modal component, forwarding the props from the 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
从<$ c读取当前 modalType
和 modalProps
它所连接的$ c> state.modal ,并呈现相应的组件,例如 DeletePostModal
或 ConfirmLogoutModal
。每个模态都是一个组件!
What have we done here? ModalRoot
reads the current modalType
and modalProps
from state.modal
to which it is connected, and renders a corresponding component such as DeletePostModal
or ConfirmLogoutModal
. Every modal is a component!
这里没有一般规则。它们只是React组件,可以调度操作,从商店状态读取内容,恰好是模态。
There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be 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
隐藏自己。
The DeletePostModal
is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal
when it is necessary to hide itself.
复制粘贴相同的布局逻辑会很尴尬每个特定模态。但你有组件,对吗?因此,您可以提取 < Modal>
组件不知道特定模态的作用,但处理它们的外观。
It would be awkward to copy-paste the same layout logic for every "specific" modal. But you have components, right? So you can extract a presentational <Modal>
component that doesn’t know what particular modals do, but handles how they look.
然后,特定模态,如 DeletePostModal
可以用它来渲染:
Then, specific modals such as DeletePostModal
can use it for 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)
由你来决定一套< Modal>
的道具可以在你的应用程序中接受,但我想你可能有几种模态(例如信息模态,确认模态等),以及几种风格对于他们。
It is up to you to come up with a set of props that <Modal>
can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.
关于模态的最后一个重要部分是通常我们想在用户点击外面或按Escape时隐藏它们。
The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.
我建议你不要自己实现它,而不是给你实现这个的建议。 。考虑到可访问性很难做到正确。
Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.
相反,我建议您使用可访问现成的模态组件,例如。它是完全可定制的,你可以把它想要的东西放在里面,但它正确处理可访问性,这样盲人仍然可以使用你的模态。
Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal
. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.
你甚至可以包裹 react-modal
在您自己的< Modal>
中,它接受特定于您的应用程序的道具并生成子按钮或其他内容。这只是组件!
You can even wrap react-modal
in your own <Modal>
that accepts props specific to your applications and generates child buttons or other content. It’s all just components!
有多种方法可以做到。
有些人不喜欢这种方法的详细程度,并且更喜欢拥有可以渲染的< Modal>
组件使用称为门户的技术在其组件内部。 Portals允许你在你的内部渲染一个组件,而实际上它将在DOM中的预定位置渲染,这对于模态来说非常方便。
Some people don’t like the verbosity of this approach and prefer to have a <Modal>
component that they can render right inside their components with a technique called "portals". Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.
实际上我链接到之前已经在内部进行了这样的技术操作,你甚至不需要从顶部渲染它。我仍然觉得从显示它的组件中分离出我要显示的模态很好,但你也可以直接从组件中使用 react-modal
,并跳过大部分内容我在上面写道。
In fact react-modal
I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal
directly from your components, and skip most of what I wrote above.
我鼓励你考虑这两种方法,试验它们,然后选择你认为最适合你的应用和你的团队的方法。
I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.
这篇关于如何在Redux中显示执行异步操作的模式对话框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!