4

Struggling to dispatch an action from my React component. This is my first Redux app. Everything seems to be working fine, but if it was I would not be posting this question. I am using Redux devTool to debug my app. If I use the dispatcher from the devTools my reducer is triggered with no problem. However I am unable to dispatch the same action from my React components. I added a breakpoint in my action to see if it was being triggered. I definately is and it is also returning a valid action (type & payload). Here is my code:

store.js

import {createStore, applyMiddleware, compose} from 'redux'
import {createLogger} from 'redux-logger'
import thunk from 'redux-thunk'
import promise from 'redux-promise-middleware'
import reducers from './reducers/index'

const logger = createLogger()

const store = createStore(
    reducers,
    window.__REDUX_DEVTOOLS_EXTENSION__ && 
    window.__REDUX_DEVTOOLS_EXTENSION__(),
    compose(
      applyMiddleware(thunk, promise, logger)
    )
)

export default store

reducers (index.js)

import {combineReducers} from 'redux'
import userReducer from './userReducer'

const allReducers = combineReducers({
    user: userReducer
})

export default allReducers

client.js

import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import store from './store'
import Router from './modules/router'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import CustomTheme from './modules/theme'
import injectTapEventPlugin from 'react-tap-event-plugin'
require('../scss/style.scss')

// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

ReactDOM.render(
  <Provider store={store}>
    <MuiThemeProvider muiTheme={CustomTheme}>
      <Router/>
    </MuiThemeProvider>
  </Provider>,
  document.getElementById('app')
);

userReducer.js

export default function (state = {loggedIn: false}, action) {
    console.log("THIS IS THE REDUCER: STATE: ", state, " - ACTION: ", action)
    switch (action.type) {
        case 'LOGIN':
            return {...state, loggedIn: action.payload}
    }
    return state;
}

userActions.js

export const login = () => {
  console.log("TEST")
    return {
        type: 'LOGIN',
        payload: true
    }
}

login.js

import React from 'react'
import ReactDOM from 'react-dom'
import LoginForm from '../containers/loginform'

class Login extends React.Component {
  render() {
    return (
      <LoginForm/>
    )
  }
}

export default Login

loginform.js

import React, {PropTypes} from 'react'
import ReactDOM from 'react-dom'
import {Redirect} from 'react-router-dom'
import {connect} from 'react-redux'
import {login} from '../actions/userActions'
import RaisedButton from 'material-ui/RaisedButton'
import TextField from 'material-ui/TextField'

class LoginForm extends React.Component {
  constructor(props) {
    super(props)
  }
  loginReq(e){
     e.preventDefault()
     this.props.login()
  }
  render() {
    return (
      <div>
        <form className='login-form-container' onSubmit=    {this.loginReq.bind(this)}>
          <div className='login-form-row'>
            <TextField
              ref='email'
              hintText='Email'
              floatingLabelText='Email'
              className='login-form-field'/>
          </div>
          <div className='login-form-row'>
            <TextField
              ref='password'
              hintText='Password'
              floatingLabelText='Password'
              type='password'
              className='login-form-field'/>
          </div>
          <div className='login-form-row'>
            <RaisedButton
              type= 'submit'
              label='Login'
              className='login-form-button'/>
          </div>
        </form>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
    return {
        loggedIn: state.user.loggedIn
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
      login: () => dispatch(login())
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(LoginForm)

Please could you give me some guidance to how else i can debug to find out why this dispatch is not working. I have tried adding the action object straight into that dispatch function. still no luck. I get no errors in the console nothing. My console.logs are only printed when the view renders and when i click on the login submit button.

Console Screenshot

  • have you checked whether there are some errors in the console? Your code seems to be correct. – Facundo La Rocca May 18 '17 at 15:04
  • I'm not sure it's actually related to your dispatching problem, but it looks like you have an error in your `createStore` call. The Redux DevTools Extension enhancer should be part of the call to `compose()`, after `applyMiddleware()`. – markerikson May 18 '17 at 21:28
  • One other suggestion: while your `mapDispatch` usage looks correct, just for kicks, try using the other way to bind an action creator: `export default connect(mapStateToProps, {login})(LoginForm)`. If that doesn't work, I'd suggest dropping in to the Reactiflux chat channels on Discord and asking for help. The invite link is at https://www.reactiflux.com . – markerikson May 18 '17 at 21:30

3 Answers3

7

Finally found my issue. My middleware implementation was causing the issue. I was passing in promise incorrectly. Should be:

import {createStore, applyMiddleware} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import {createLogger} from 'redux-logger'
import thunk from 'redux-thunk'
import promise from 'redux-promise-middleware'
import reducers from './reducers/index'

const logger = createLogger()

const middleware = applyMiddleware(promise(), logger, thunk)

const store = createStore(reducers, composeWithDevTools(middleware))

export default store

Also found that redux-devtools-extension was cleaner for Redux devTools.

Enrico
  • 766
  • 8
  • 19
1

My hunch would be how you are trying to invoke the function to dispatch the action. Firstly, bind the function to this in the component constructor (See the React docs on event handlers here for more info). Secondly, just pass the function to onSubmit.

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

  loginReq(e) {
    e.preventDefault()
    this.props.login()
  }

  render() {
    return (
      <div>
        <form className='login-form-container' onSubmit={this.loginReq}>
          <div className='login-form-row'>
            <TextField
              ref='email'
              hintText='Email'
              floatingLabelText='Email'
              className='login-form-field'/>
          </div>
          <div className='login-form-row'>
            <TextField
              ref='password'
              hintText='Password'
              floatingLabelText='Password'
              type='password'
              className='login-form-field'/>
          </div>
          <div className='login-form-row'>
            <RaisedButton
              type= 'submit'
              label='Login'
              className='login-form-button'/>
          </div>
        </form>
      </div>
    )
  }
}

An alternative way to bind the function to this is to remove the bind statement in the constructor and use an arrow function for the form prop, like this:

onSubmit={e => this.loginReq(e)}
Mike
  • 6,149
  • 5
  • 34
  • 45
  • Hey Michael, thank you for the knowledge share. Unfortunately it is still not working. This should be so simple. Starting to get slightly frustrated. I have watched numerous videos and it seems that my implementation should work. I have even tried hardcoding the action in the dispatch function and it is still not triggering my reducer. Not sure how to debug the connection from my action to my reducer as my action function is definitely getting triggered. – Henry John Nieuwenhuizen May 18 '17 at 18:01
  • Are your event handlers getting called at every stage up the chain? Are there any exceptions? If you have a debugger then breakpoint every step from clicking the button to hitting the reducer. Failing a debugger, resort to good old fashioned console.log at every stage you think the code should be hitting. I'm not familiar with Redux Dec tools extension, but I'd also be inclined to strip your store creation back to basics and build up. Are you able to do a tiny standalone barebones Redux example to ensure you can make it work that way? – Mike May 18 '17 at 22:11
0

modify action:

export const login = () => {
  return function (dispatch) {
    console.log('here');
    dispatch({
        type: 'LOGIN',
        payload: true
    });
  }
}

I guess you'd like => syntax in that case.

Alexey Avdeyev
  • 599
  • 9
  • 20