React-Redux Action: 'Dispatch' is not a function
You have to do something like this:
export const getMetaOutsider = () => {
return (dispatch) => {
Axios.get("meta/outsiders")
.then(res => {
dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data });
})
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
}
};
Try this, It should work. Feedbacks are welcome. redux-thunk handles functions passed as the argument to dispatch instead of objects.
Related videos on Youtube
Chris Rutherford
By day, wrangling applications and writing replacements for some at a wonderful Non-Profit, and by night, Trying to take over the world with fresh ideas.
Updated on June 04, 2022Comments
-
Chris Rutherford almost 2 years
Still getting used to Redux, first off. I have a component that should simply load data for display when the component loads. I have redux setup with the store:
//store.js import { createStore, applyMiddleware, compose } from 'redux'; import logger from 'redux-logger'; import thunk from 'redux-thunk'; import root from './reducers'; const middleware = [thunk, logger]; const initState = {}; const store = createStore( root, initState, compose( applyMiddleware(...middleware), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ) ); export default store;
and all the reducers that I'll need in a full on combine reducers file:
//{projectFolder}/reducers/index.js import { combineReducers } from 'redux'; import authReducer from './authReducer'; import errorsReducer from './errorReducer'; import suggestionReducer from './suggestionReducer'; import insiderReducer from './insiderReducer'; import connectionReducer from './connectionReducer'; import outsiderReducer from './outsiderReducer'; import contactReducer from './contactReducer'; import metaReducer from './metaReducer'; export default combineReducers({ auth: authReducer, errors: errorsReducer, suggestions: suggestionReducer, insider: insiderReducer, connection: connectionReducer, outsider: outsiderReducer, contact: contactReducer, meta: metaReducer });
The one that I'm interested in is the
metaReducer
which is the called by an action, or so it should be.//metaReducer.js import {GET_INSIDER_META_INFORMATION, GET_OUTSIDER_META_INFORMATION } from '../actions/types'; const initState = { insider: {}, outsider: {} }; export default (state = initState, { type, payload }) => { switch (type) { case GET_INSIDER_META_INFORMATION: return{ ...state, insider: payload } case GET_OUTSIDER_META_INFORMATION: return { ...state, outsider: payload } default: return state; } };
The meta reducer is just to house the information coming from the back-end and is each case of the reducer is called from the
actions/meta.js
file which looks like this://{projectfolder}/actions/meta.js import { GET_INSIDER_META_INFORMATION, GET_OUTSIDER_META_INFORMATION, POPULATE_ERRORS } from "./types"; import Axios from "axios"; export const getMetaInsider = (dispatch) => { return Axios.get("meta/insiders") .then(res => dispatch({ type: GET_INSIDER_META_INFORMATION, payload: res.data }) ) .catch(err => dispatch({ type: POPULATE_ERRORS, payload: err.response.data }) ); }; export const getMetaOutsider = (dispatch) => { return Axios.get("meta/outsiders") .then(res => { dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data }); }) .catch(err => dispatch({ type: POPULATE_ERRORS, payload: err.response.data }) ); };
and My component that calls all of this is setup as below:
//{projectfolder}/components/home.js import React, {Component} from 'react'; import {Card, CardTitle, CardSubtitle, CardBody} from 'reactstrap'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import {getMetaInsider, getMetaOutsider} from '../actions/meta'; class Home extends Component{ constructor(props){ super(props); this.state = { insider:{}, outsider: {} } } componentDidMount() { console.log(this.props); this.props.getMetaInsider(); this.props.getMetaOutsider(); } render(){ let {insiders, outsiders} = this.state; return( <React.Fragment> {*/ omitted as it's not really an issue right now, data is more important than layout /*} </React.Fragment> ) } } const mapState = state => { console.log(state); return { insider: state.meta.insider, outsider: state.meta.outsider } }; Home.propTypes = { getMetaInsider: PropTypes.func.isRequired, getMetaOutsider: PropTypes.func.isRequired, insider: PropTypes.object.isRequired, outsider: PropTypes.object.isRequired }; export default connect(mapState, {getMetaInsider, getMetaOutsider})(Home);
So when the component loads, I get a horribly weird issue where it looks like jquery is being called, and it's imported in my App.js file for bootstrap. However, the main error is this:
"TypeError: dispatch is not a function at http://localhost:3000/static/js/bundle.js:73524:22"
Which maps up to the
.catch
block of thegetMetaInsider
function.-
simbathesailor over 5 yearsYou need to wrap your async action creators return functions inside another function.
-
-
Chris Rutherford over 5 yearsI had a previous project from a long tutorial that did that and i didn't understand why I had to do that. Now I do! Thanks @simbathesailor
-
simbathesailor over 5 yearsso what happens, is you pass the actions bound with dispatch function to your dumbcomponents(using bindActioncreators or manually). Now, whenever you call a function bound with dispatch you, generally call it and if the action is tend to do something asynchronous, you need to return a function out of it , not a value. That's where redux -thunk comes in the picture and handles the dispatch flow. redux-thunk will take the returned function and call it with passing store.dispatch as the argument. thats how it is available in your returned function. Thanks. read more at redux website. Thanks