Sourav
Sourav

Reputation: 504

dispatch is not defined inside async action creators

I am trying to dispatch another action inside one async action creator. However, when I am using dispatch function provided by redux-thunk middleware, it is throwing Uncaught TypeError: dispatch is not a function error.

Below are the files for more help

store/store.dev.js

use strict';

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducers';
import ReduxThunk from 'redux-thunk';

export default function configureStore(initialState = {}) {
const store = createStore(rootReducer, initialState, applyMiddleware(ReduxThunk));
  console.log('store returned', store);
return store;
}

src/index.js

'use strict';

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import configureStore from './store';
import App from './containers/App.js';
import routes from './routes/index.js';

hydrate(
  <Provider store={configureStore()}>
    <BrowserRouter>
      {renderRoutes(routes)}
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

reducer

'use strict';

import { LOGIN_SUBMITTED, LOGIN_COMPLETED } from '../constants/ActionTypes.js';

const loginSubmitReducer = (state = [], action) => {
  console.log('in reducer');
    switch (action.type) {

      case LOGIN_SUBMITTED:
      console.log('login submitted case');
        return Object.assign({}, state, {
          loginSubmitted: true
        });

      case LOGIN_COMPLETED:
      console.log('login completed case');
        return Object.assign({}, state, {
          loginCompleted: true
        });

      default:
      console.log('in default case');
        return {
          a:1
        };
    }
}

export default loginSubmitReducer;

containers/App.js

'use strict';

import React from 'react';
import { loginCompleted, loginSubmitted } from '../actions';
import { createStructuredSelector, createSelector } from 'reselect';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Header, ButtonDemo, LoginForm } from '../components';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

class App extends React.Component {
  constructor(props, context) {
    super(props);
    this.state = {
      appState: {
        entry: 'yes'
      }
    }

    console.log('props in App constructor', props);
    console.log('context in App constructor', context);

    this.newFunc = this.newFunc.bind(this);
  }
  newFunc () {
    console.log('props', this.props);
  }
  render() {
    console.log('props in main App', this.props);
    console.log('Actions', this.props.actions);
    console.log('context in App', this.context);
    // this.props.actions.loginCompleted();
    const muiTheme = getMuiTheme({
      lightBaseTheme
    });

    return (
      <MuiThemeProvider muiTheme={muiTheme}>
        <div>
            <Header />
            <LoginForm 
              appstate = {this.props.appState}
              dispatchAction = {this.props.actions}
            />
        </div>
      </MuiThemeProvider>
    );
  }
}

// const mapStateToProps = createStructuredSelector({});
const mapStateToProps = (state) => {
  return { appState: state.loginSubmitReducer };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators({ loginCompleted, loginSubmitted }, dispatch)
  };
  // dispatch(loginCompleted());
}

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

components

    'use strict';

import React, { PropTypes } from 'react';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ContentAdd from 'material-ui/svg-icons/content/add';
import { loginAction } from '../actions';
// import '../css/style.css';


class LoginForm extends React.Component {
    constructor(props) {
        super(props);
        this.loginSubmit = this.loginSubmit.bind(this);
    }

    loginSubmit() {
        console.log('Login fomr submitted', this.props);
        console.log('loginAction', loginAction);
        loginAction()();
    }

    render () {
        console.log('props when login rendered', this.props);
        return (
            <div className="loginForm">
                <div className="title">LOGIN</div>
                <form>
                    <TextField 
                        hintText="Username"
                        floatingLabelText="Username"/><br/>
                    <br/>
                    <TextField
                        hintText="Password"
                        type="Password"
                        floatingLabelText="Password"/><br/>
                    <br/>
                    <RaisedButton
                        label="Login" 
                        secondary={true} 
                        onClick={this.loginSubmit}
                        className="submitBtn" /><br/>
                    <br/>
                    <FloatingActionButton                       
                        secondary={true}
                        className="registerBtn">
                        <ContentAdd />                      
                    </FloatingActionButton>
                </form>
            </div>
        )
    }
}

export default LoginForm;

actions/loginAction.js

'use strict';

// import { polyfill } from 'es6-promise';
// require('es6-promise').polyfill();
// require('isomorphic-fetch');
import request from 'superagent';
// import fetch from 'isomorphic-fetch';
import loginCompleted from './loginCompletedAction.js';
import loginSubmitted from './loginSubmittedAction.js';

const loginAction = (dispatch) => {
    console.log('validateUserLogin executed', this);
    console.log('validateUserLogin dispatch', dispatch);
    return (dispatch) => {
        // console.log('first return', dispatch);
        // this.props.dispatch(loginSubmitted());
        // this.props.dispatchAction.loginSubmitted();
        dispatch(loginSubmitted());

        request.get('http://localhost:3000/loginSubmit')
            .end((err, res) => {
                console.log('res', res);
                console.log('err', err);
                // this.props.dispatchAction.loginCompleted();
            });
    }
}

export default loginAction;

Folder structure

Project structure

I am using redux-thunk middleware that exposes dispatch function to async action creators. Still it doesn't work.

Please help!

Upvotes: 3

Views: 1661

Answers (1)

Sagiv b.g
Sagiv b.g

Reputation: 31024

Your first argument of the action creator should not be the dispatch but the argument you pass to the action creator.

const loginAction = () => (dispatch) => {
    console.log('validateUserLogin executed', this);
    console.log('validateUserLogin dispatch', dispatch);
    // console.log('first return', dispatch);
    // this.props.dispatch(loginSubmitted());
    // this.props.dispatchAction.loginSubmitted();
    dispatch(loginSubmitted());

    request.get('http://localhost:3000/loginSubmit')
        .end((err, res) => {
            console.log('res', res);
            console.log('err', err);
            // this.props.dispatchAction.loginCompleted();
        });
}

Edit
As a follow up to your comment, iat first i thought you are not implementing the redux-thunk properly as a middle-ware but according to your code it looks ok.
Then I've noticed that you are not really dispatching loginAction but just invoking it.
You should dispatch it dispatch(loginAction()). Your component should be connected to redux then you will have access to dispatch.

Upvotes: 2

Related Questions