Search code examples
reactjsreduxreact-reduxrecompose

React, Redux and Recompose: "dispatch is not a function"


I've got a container/component (from Redux examples) complaining about "dispatch is not a function". I had this working before I added Recompose. I think Recompose puts a wrapper around dispatch(), so I need to expose it somehow. Maybe applyMiddleware will do the trick, but I don't know where to hook it up? What do I need to do?

Container:

const AddTodo = (props, dispatch) => {
  let input;
  const { classes } = props;
  return (
    <div>
      <form
        id="my-form-id"
        onSubmit={e => {
          e.preventDefault();
          if (!input.value.trim()) {
            return;
          }
          dispatch(addTodo(input.value));//<<<OFFENDING LINE
          input.value = "";
        }}
      >
        <TextField
          id="agentName"
          label="Agent Name"
          placeholder="Placeholder"
          form="my-form-id"
          inputRef={el => (input = el)}
          className={classes.textField}
          margin="normal"
        />
        <Button variant="extendedFab" type="submit" className={classes.button}>
          <AddIcon className={classes.addIcon} />
          New Todo
        </Button>
      </form>
    </div>
  );
};

export default compose(
  withStyles(styles),
  connect()
)(AddTodo);

Root index.js:

import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import App from "./components/App";
import rootReducer from "./reducers";

const store = createStore(rootReducer);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

Solution

  • There are two basic things to understand.

    1. When composing connect() Redux adds dispatch as a prop.

    export default compose(
      withStyles(styles),
      connect() // <-- This adds dispatch to props.
    )(AddTodo);
    

    2. You should access props as a single object or destructure branches of the props object.

    This line is where the misunderstanding is happening.

    const AddTodo = (props, dispatch) => { // <-- dispatch is not an parameter, it exists at props.dispatch
    

    To fix things using your existing pattern do this.

    const AddTodo = (props) => {
      let input;
      const { classes, dispatch } = props;
      return (
      ...
    

    Optionally you can destructure the props parameter directly.

    const AddTodo = ({ classes, dispatch }) => {
      let input;
      return (
      ...
    

    With either approach the remaining code will work as expected.