Dispatch another action / call another reducer within a reducer

Viewed 961

I am using React with Redux and have ran into a situation where I want one of my reducers to dispatch another action, but am not sure how.

Created using the createSlice function, I have a slice for authentication and one for modals. I would like to call the hideModal action from within the login reducer of my authentication slice, to cleanly hide the login modal once submitted.

The reason I cannot just call dispatch(login()) immediately followed by dispatch(hideModal()) is that I need to find out if there are any errors in the form data first, which can only be known inside the [login.fulfilled] reducer, as login is an asynchronous thunk, created using createAsyncThunk.

I understand that this is somewhat of an anti-pattern in React, so was wondering what is the best way to achieve this?

The only solution I have so far is for the modalSlice to listen for login.fulfilled as part of its extraReducers object. But this is quite messy.

Any help would be appreciated, do let me know if this question is not clear enough, I will try and clarify. Thank you

Authentication Slice

export const authSlice = createSlice({
    name: "auth",
    initialState: {
        loggedIn: false,
        userId: null,
        errors: []
    },
    extraReducers: {
        [login.fulfilled]: (state, action) => {
            if (action.payload.success) {
                state.loggedIn = true;
                state.userId = action.payload.userId;
                state.errors = [];
            } else {
                state.errors = action.payload.errors;
            }
        },
// Omitting rest of slice

Modal Slice

export const modalSlice = createSlice({
    name: "modal",
    initialState: {
        current: ""
    },
    reducers: {
        hideModal: (state) => {
            state.current = "";
        },
        showModal: (state, action) => {
            state.current = action.payload;
        }
    },

    // Possible solution: Works but quite messy 
    // and copies code from above rather than calling hideModal
    extraReducers: {
        [login.fulfilled]: (state, action) => {
            if (action.payload.success) {
                state.current = "";
            }
        },
// Omitting rest of slice
1 Answers

The reason I cannot just call dispatch(login()) immediately followed by dispatch(hideModal()) is that I need to find out if there are any errors in the form data first, which can only be known inside the login reducer.

Can't you do a check for any errors before the next dispatch? You have access to the redux store inside of your component

dispatch(authLogin);
if (this.state.auth.errors) { // or however you are connecting to your store
  // do error handling stuff
} else {
 dispatch(closeModal);
}
Related