Confirmation Dialog on top of User Edit Dialog

A Redux-Saga Implementation of Modal Confirmation Dialogs in React + Redux

last updated: Sep 17th, 2016

Want to add a modal confirmation dialog to your React + Redux application?

There are already a few good blogs and tutorials on how to do this in React + Redux. However, when I needed to add a confirmation to an app I was working on, I found that everyone who came before me seemed to focus on thunks, which are difficult to test.

Using thunks doesn't work for me because I prefer redux-saga for encapsulating workflow. I also wanted to avoid solutions that keep state in the UI components.

Thanks to redux-saga, it was easy to create a confirmation workflow that fit seamlessly into my code.

The example I'll show here is for deleting a user.

The saga for deleting a user started out as a very basic saga. As a side note, I implement all my logic this way. There is some repetition/boilerplate but overall it makes reasoning about an application very easy. Anyhow... the deleteUserSaga waited for a delete-user request, fired off a RESTful delete request to the server, and then reported success/failure. It looked like this:

userSagas.ts -- Before

export function* deleteUserSaga() {    while (true) {        const { payload: id } = yield take(Actions.DELETE_USER_REQUEST);        try {            yield call(apiClient.delete, `/api/users/${id}`);            yield put(deleteUserSuccess(id));        }        catch (err) {            yield put(deleteUserFailure(err, id));        }    }}

Redux-saga allows us to call sagas from within other sagas (read more here). And I'll be using the confirmation dialog in a few places. So it makes sense to create a new saga dedicated to handling the confirmation dialog logic.

confirmSaga.ts

import { take, put, race } from 'redux-saga/effects';import {    Actions as ShellActions,    showConfirmation, hideConfirmation} from '../actions/shellActions';export function* confirmSaga(confirmationMessage: string) {    // Cause the dialog to be shown (reducer will put the message    // in the store; the main shell UI component will receive the    // message in its props and then display the dialog)     yield put(showConfirmation(confirmationMessage));    // Wait for either a yes or a no.    // The dialog UI component receives yes and no event handlers    // in its props that dispatch these actions.    const { yes } = yield race({        yes: take(ShellActions.CONFIRM_YES),        no: take(ShellActions.CONFIRM_NO)    })    // Tell a reducer to hide the dialog    yield put(hideConfirmation());    // Returns true if the 'yes' action was received    return !!yes;}

After having added the confirmation dialog, deleteUserSaga now looks like this:

userSagas.ts -- After

export function* deleteUserSaga() {    while (true) {        const { payload: id } = yield take(Actions.DELETE_USER_REQUEST);        // Convert the user ID into a display name for the user        const user = yield select(getUser, { id });        const name = user.get('displayName') || user.get('login') || '';        // Jump into the confirm saga to handle confirmation logic        const message = t('USERS.DELETE_CONFIRMATION', { name });        const confirmed = yield call(confirmSaga, message);        if (!confirmed) {            continue;        }        try {            yield call(apiClient.delete, `/api/users/${id}`);            yield put(deleteUserSuccess(id));        }        catch (err) {            yield put(deleteUserFailure(err, id));        }    }}

Further Reading

Other modal dialogs in React + Redux:

Building asynchronous web applications is complicated. React with Redux is not enough. You need something like Redux-Saga to complete the picture.

I can show you how. Sign up on my email list where I write about Redux-Saga and related web development topics.