This post is mostly a reminder for myself, but someone else using React and Redux may find it helpful.

If you repeatedly call a Redux action and are boggled by the fact that the action isn’t actually being dispatched, make sure you aren’t just importing and calling the action creator. You need to either call dispatch(someActionCreator()) explicitly, or bring the action in via using mapDispatchToProps on a component. This adds a wrapper function to props on that component which has dispatch() built in.

The first time I saw this usage of mapDispatchToProps (aka react-redux’s object shorthand form) in someone else’s code, I was very confused because I never saw an explicit call to dispatch() anywhere. It seemed to be magically dispatching all on its own.

Here is an example from the code for a project of mine, xpromo.poddinglabs.com.

The action creator that I am using is called setFilterPodcast:

PodcastCard.js (a component)

...
import { connect } from "react-redux";
import { 
	getPodcastQueryRequest, 
	setFilterPodcast } from "../../actions";

...

const PodcastCard = (props) => {
	
	const { setFilterPodcast } = props;

	...

	}

// https://react-redux.js.org/api/connect#object-shorthand-form
// Bind action creators to dispatchable functions in props.
// i.e. adding getDataRequest here will create
// this.props.getDataRequest() for the component, which dispatches this action
const mapDispatchToProps = {
	setFilterPodcast,
};

export default connect(null, mapDispatchToProps)(PodcastCard);

(Note that here I do not need a mapStateToProps for accessing any part of the store, so I pass in null into the first argument of connect().)

../../actions/index.js

import { DataTypes } from "./data";
import { FilterTypes } from "./filter";
export * from "./data";
export * from "./filter";

export const Types = {
	...DataTypes,
	...FilterTypes
};

../../actions/filters.js


// DEFINE NEW ACTION TYPES HERE
const Types = {
	SET_FILTER_PODCAST: "set_filter_podcast",
};

export const setFilterPodcast = (podcastName, podcastTags) => ({
	type: Types.SET_FILTER_PODCAST,
	payload: {
		podcastName,
		podcastTags
	}
});

export const FilterTypes = Types;

A very easy mistake to make - and I have done this several times and yelled at the computer for hours - is to simply import the action creator, setFilterPodcast, and then call that directly.

You won’t get any errors if you do this, because it is a valid function.

But of course, your action won’t dispatch, and you’ll be left scratching your head, wondering why your action didn’t have its intended effect.

Use redux-logger to monitor actions and store state!

Lastly, I suggest using redux-logger as middleware in your dev environment, which will kick out useful info about store updates in your console. This makes it incredibly obvious when an action is not dispatching at all.

Here’s an example of how I use it in my store setup, alongside redux-saga:

store.js

import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import logger from "redux-logger";

import reducers from "../reducers";
import rootSaga from "../sagas";

// create the saga middleware
const sagaMiddleware = createSagaMiddleware();

let middlewareList;
if (process.env.REACT_APP_ENVIRONMENT === "production") {
	middlewareList = [sagaMiddleware];
} else {
	middlewareList = [sagaMiddleware, logger];
}

const store = createStore(reducers, applyMiddleware(...middlewareList));

// then run the saga
sagaMiddleware.run(rootSaga);

export default store;

And you’ll get useful output as follows when an action dispatches to alter the store: