How do I manually dispatch actions to a store created with configureStore?

Viewed 1111

I have a project where half of it was made with classes, and the other half is being made with hooks and Redux. For this, I have created a store with configureStore() from Redux Toolkit and provided it using the Provider component. In a very minimal way, the store is set up as follows:

const userSlice = createSlice({
    name: 'user',
    initialState: {
        user: {}
    },
    reducers: {
        validate: (state, action) => state.user = action.payload
    }
})

const store configureStore({
    reducer: {
        user: userSlice.reducer
    }
})

There are two components - a new one, functional, which uses the useSelector() hook, and an older one, which is class based, but needs to use this sasme store to dispatch an action. To do this, I import the store and fire

store.dispatch({type: 'user/validate', payload: newUser});

from the class component. I receive no errors, yet nothing happens at all.

I tracked my input from DevTools' Redux plugin, and I can see the state does not change, so I assume my manual call to dispatch is somehow wrong.

What I expect to happen is for the state to update, which would trigger a re-render of the component that uses useSelector

2 Answers

The following way is a safe way to dispatch actions without misspelling the type string.

  1. Extract the action from reducer
const userSlice = createSlice({
    name: 'user',
    initialState: {
        user: {}
    },
    reducers: {
        validate: (state, action) => {
            state.user = action.payload
          }
    }
})

// <------------------
// Action creators are generated for each case reducer function
export const { validate } = userSlice.actions


export const store =  configureStore({
    reducer: {
        user: userSlice.reducer
    }
})

  1. Dispatch the action as so
store.dispatch(validate(newUser))

Issue

I'm going to say the issue is that you are trying to both mutate your state object in the reducer function and return it.

See Mutating and Returning State

In any given case reducer, Immer expects that you will either mutate the existing state, or construct a new state value yourself and return it, but not both in the same function!

Solution

Just mutate the state, don't return it.

reducers: {
  validate: (state, action) => {
    state.user = action.payload;
  },
}

If you want your class-based component to subscribe to your redux store then you can still use the connect Higher Order Component from react-redux.

Example:

import { connect } from 'react-redux';
import { validate } from '../path/to/userSlice';

class MyComponent extends Component {

  ...

    // In a function you can simply dispatch the validate action
    // as it was wrapped in a call to dispatch already and injected
    // as a prop.
    this.props.validate(somePayloadValue);

  ...

}

const mapDispatchToProps = {
  validate
};

export default connect(null, mapDispatchToProps)(MyComponent);
Related