Search code examples
reactjsreduxreact-reduxredux-thunk

TypeError: dispatch is not a function in my thunks


I have the error:

TypeError: dispatch is not a function

in this line of code dispatch(addNewNote(response.data.message));

I don't know why this error is happening, since I have other dispatches in other functions within the thunks.js file working just fine.

I really need help, any advice would be appreciated.

Please consider the code below:

binnacleReducer.js

   import { ADD_NEW_NOTE } from "../Actions/binnacleActions";

    export const binnacleNotesReducer = (state = [], action) => {
     const { type, payload } = action;
     switch (type) {

    case ADD_NEW_NOTE:
      const { binnacleNote } = payload;
       return state.concat(binnacleNote);
      default:
       return state;
  }
};

binnacleActions.js

//Action creators and actions

    export const ADD_NEW_NOTE = "ADD_NEW_NOTE";
    export const addNewNote = (binnacleNote) => ({
     type: ADD_NEW_NOTE,
     payload: {
      binnacleNote,
     },
    });

thunks.js

import axios from "axios";
import { loginSuccess, loginFailed } from "../Actions/authActions";
import {
  setOwnerSetup,
  setSuperSetup,
  loadBinnacleNotes,
  binnacleNoteReview,
  addNewNote,
} from "../Actions/binnacleActions";

export const addBinnacleNote = (
  binnacle,
  noteNumber,
  binnacleNote,
  responsible,
  file
) => async (dispatch, getState) => {
  try {
    const bodyToSend = new FormData();
    bodyToSend.append("binnacle", binnacle);
    bodyToSend.append("binnacleNote", binnacleNote);
    bodyToSend.append("noteNumber", noteNumber);
    bodyToSend.append("responsible", responsible);

    if (file) {
      for (let x = 0; x < file.length; x++) {
        bodyToSend.append(`file[${x}]`, file[x]);
      }
    }

    const response = await axios.post(
      "http://localhost:5000/addBinnacleNote",
      bodyToSend,
      {
        headers: {
          "Content-Type": "application/json",
        },
      }
    );
    dispatch(addNewNote(response.data.message));
  } catch (e) {
    alert("Thunk error in addNote: " + e);
  }
};

EDIT:

BinnacleForm.js

import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { addBinnacleNote } from "../../Store/Thunks/thunks";

import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField";
import Container from "@material-ui/core/Container";
import { Button } from "@material-ui/core";
import AccordionTest from "./AccordionTest";

var noteNumber;

function BinnacleForm({ authReducer, binnacleNotesReducer }) {
  const [binnacleNote, setBinnacleNote] = useState(null);
  const [file, setFile] = useState(null);
  const responsible = authReducer.role;
  const binnacle = authReducer.binnacle;

  const setBinnacleId = () => {
    if (binnacleNotesReducer !== [] || binnacleNotesReducer.length === 0) {
      noteNumber = 1;
    } else {
      noteNumber = binnacleNotesReducer[binnacleNotesReducer.length - 1].id + 1;
    }
  };

  setBinnacleId();

  return (
    <React.Fragment>
      <Container maxWidth="md">
        <Typography variant="h6" gutterBottom>
          Agregar Nota
        </Typography>
        {/* <AccordionTest /> */}
        <Grid container spacing={3}>
          <Grid item xs={12} sm={9}>
            <TextField
              required
              id="binnacle_note"
              name="binnacle_note"
              fullWidth
              autoComplete="given-name"
              helperText="Nota"
              onChange={(e) => setBinnacleNote(e.target.value)}
            />
          </Grid>
          <Grid item xs={12} sm={3}>
            <Button variant="contained" component="label">
              Adjuntar Archivo
              <input
                onChange={(e) => setFile(e.target.files)}
                type="file"
                hidden
                multiple
              />
            </Button>
          </Grid>
          <Button
            variant="contained"
            color="primary"
            onClick={addBinnacleNote(
              binnacle,
              noteNumber,
              binnacleNote,
              responsible,
              file
            )}
          >
            Agregar Nota
          </Button>
        </Grid>
      </Container>
    </React.Fragment>
  );
}

const mapStateToProps = (state) => ({
  binnacleReducer: state.binnacleReducer,
  authReducer: state.authReducer,
  binnacleNotesReducer: state.binnacleNotesReducer.binnacleNotes,
});

const mapDispatchToProps = (dispatch) => ({
  addBinnacleNote: (binnacle, binnacleNote, responsible) =>
    dispatch(addBinnacleNote(binnacle, binnacleNote, responsible)),
});

export default connect(mapStateToProps, mapDispatchToProps)(BinnacleForm);

EDIT 2:

BinnacleForm.js

import React, { useEffect } from "react";
import { connect } from "react-redux";
import SetupBinnacleSuper from "../Binnacle/SetupBinnacleSuper";
import SetupBinnacleOwner from "../Binnacle/SetupBinnacleOwner";
import {
  getBinnacleStatus,
  getBinnacleStatusBySuper,
} from "../../Store/Thunks/thunks";
import BinnacleNotesList from "../Binnacle/BinnacleNotesList";

const Binnacle = ({
  authReducer,
  binnacleReducer,
  getBinnacleStatus,
  getBinnacleStatusBySuper,
}) => {
  const binnacle = authReducer.binnacle;

  const renderConditionaly = () => {
    if (authReducer.role == "owner" && binnacleReducer.binnacleIsActive == 0) {
      return <SetupBinnacleOwner />;
    } else if (
      authReducer.role == "owner" &&
      binnacleReducer.binnacleIsActive == 1
    ) {
      console.log("If Owner");
      return <div>Owner</div>;
    } else if (
      authReducer.role == "super" &&
      binnacleReducer.binnacleIsActiveBySuper == 0
    ) {
      return <SetupBinnacleSuper />;
    } else if (
      authReducer.role == "super" &&
      binnacleReducer.binnacleIsActiveBySuper == 1
    ) {
      return <BinnacleNotesList />;
    } else if (authReducer.role == "dro") {
      return <BinnacleNotesList />;
    } else if (authReducer.role == "constructor") {
      return <BinnacleNotesList />;
    }
  };

  useEffect(() => {
    getBinnacleStatus(binnacle);
    getBinnacleStatusBySuper(binnacle);
  }, []);

  return (
    <div>
      <h2>Bienvenido {authReducer.email}</h2>
      {renderConditionaly()}
    </div>
  );
};

const mapStateToProps = (state) => ({
  authReducer: state.authReducer,
  binnacleReducer: state.binnacleReducer,
});

const mapDispatchToProps = (dispatch, getState) => ({
  getBinnacleStatus: (binnacle) => dispatch(getBinnacleStatus(binnacle)),
  getBinnacleStatusBySuper: (binnacle) =>
    dispatch(getBinnacleStatusBySuper(binnacle)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Binnacle);

binnacleReducer.js

export const binnacleReducer = (state = [], action) => {
  const { type, payload } = action;

  switch (type) {
    case SET_OWNER_SETUP:
      const { binnacleIsActive } = payload;
      return {
        ...state,
        binnacleIsActive,
      };
    case SET_SUPER_SETUP:
      const { binnacleIsActiveBySuper } = payload;
      return {
        ...state,
        binnacleIsActiveBySuper,
      };
    default:
      return state;
  }
};

binnacleActions.js

export const SET_SUPER_SETUP = "SET_SUPER_SETUP";
export const setSuperSetup = (binnacleIsActiveBySuper) => ({
  type: SET_SUPER_SETUP,
  payload: {
    binnacleIsActiveBySuper,
  },
});

thunks.js

import axios from "axios";
import { loginSuccess, loginFailed } from "../Actions/authActions";
import {
  setOwnerSetup,
  setSuperSetup,
  loadBinnacleNotes,
  binnacleNoteReview,
  addNewNote,
} from "../Actions/binnacleActions";

export const getBinnacleStatusBySuper = (binnacle) => async (
  dispatch,
  getState
) => {
  try {
    const response = await axios.get(
      `http://localhost:5000/getBinnacleStatusBySuper?binnacle=${binnacle}`
    );
    dispatch(
      setSuperSetup(response.data.binnacleIsActiveBySuper.is_active_by_super)
    );
  } catch (e) {
    alert(e);
  }
};

Solution

  • The issue is that you're not dispatching the action returned by addBinnacleNote. And the onClick prop should be a function.

    You've defined the mapDispatchToProps function to return an addBinnacleNote prop which dispatches the action but you're not using it inside the component. You're calling the imported addBinnacleNote function instead. There are a couple of ways to solve it. You could remove the mapDispatchToProps function and use useDispatch to get access to the dispatch function and dispatch the action inside the onClick handler.

    import { useDispatch } from 'react-redux'
    
    const dispatch = useDispatch()
    
    <Button
      variant="contained"
      color="primary"
      onClick={() =>
        dispatch(
          addBinnacleNote(binnacle, noteNumber, binnacleNote, responsible, file)
        )
      }
    >
      Agregar Nota
    </Button>
    

    Or you could use the addBinnacleNote prop inside the onClick handler.