I'm building an app that needs to show a confirm dialog in some situations.
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.
- How can this component dispatch the proper action according to the first action dispatched?
- Should the action creator handle this logic?
- Can we add actions inside the reducer?
edit:
to make it clearer:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
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.
Best Answer
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:
Dispatching an Action to Show the Modal
(Strings can be constants of course; I’m using inline strings for simplicity.)
Writing a Reducer to Manage Modal State
Then make sure you have a reducer that just accepts these values:
Great! Now, when you dispatch an action,
state.modal
will update to include the information about the currently visible modal window.Writing the Root Modal Component
At the root of your component hierarchy, add a
<ModalRoot>
component that is connected to the Redux store. It will listen tostate.modal
and display an appropriate modal component, forwarding the props from thestate.modal.modalProps
.What have we done here?
ModalRoot
reads the currentmodalType
andmodalProps
fromstate.modal
to which it is connected, and renders a corresponding component such asDeletePostModal
orConfirmLogoutModal
. Every modal is a component!Writing Specific Modal Components
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.
For example,
DeletePostModal
might look like:The
DeletePostModal
is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, includinghideModal
when it is necessary to hide itself.Extracting a Presentational Component
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.Then, specific modals such as
DeletePostModal
can use it for rendering: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.Accessibility and Hiding on Click Outside or Escape Key
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.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!Other Approaches
There is more than one way to do it.
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.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 usereact-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.