3

As the title says, I'm having issues with my React app where I'm able to hit the Redux Action, but it does not hit the Reducer after that. I've looked at a past project I worked on, as well as several posts on here, but I'm not certain what is wrong with my code that's preventing the Action from hitting the reducer. I've pasted the code below, but please let me know if there's anything else I can provide.

index.js:

import React from 'react';
import ReactDom from 'react-dom';
import App from './components/App.jsx';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer from './reducers/usersRedcuers';
import './index.css';

const store = createStore(reducer);

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

App.jsx Component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import Auth from '../modules/Auth';
import { login } from '../actions/usersActions';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      auth: null,
      token: '',
      password: '',
      username: '',
    }

    const {
      login,
    } = this.props;
  }



  loginHandler() {
    const { password, username } = this.state;
    const auth = Auth.isUserAuthenticated()
    const token = null;

    login(auth, token, password, username);   
  };

  render() {
    return (
      <div className="App">
        <div className="title">
          Recipe Box
        </div>
        <div className="form-inline login-form">
          <div className="form-group">
            <input
              className="form-control"
              onChange={e => this.setState({ username: e.target.value })}
              placeholder="Username"
            />
            <input
              className="form-control"
              onChange={e => this.setState({ password: e.target.value })}
              placeholder="Password"
              type="password"
            />
          </div>
          <button
            className="btn btn-success"
            onClick={() => this.loginHandler()}
            type="button"
          >
            Login
          </button>
        </div>
      </div>
    );
  }    
}

function mapDispatchToProps(dispatch) {
  console.log('dispatching:', dispatch)
  return bindActionCreators({login}, dispatch);
}

function mapStateToProps(state) {
  return { state }
}

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

Action Constants:

// AUTH
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';

// USERS
export const ADD_USER = 'ADD_USER';
export const DELETE_USER = 'DELETE_USER';
export const UPDATE_USER = 'UPDATE_USER';

Actions:

import {
  ADD_USER,
  DELETE_USER,
  LOGIN,
  LOGOUT,
  UPDATE_USER,
} from '../constants/constants';

export const login = (auth, token, password, username) => {
  const action = {
    type: LOGIN,
    auth,
    token,
    password,
    username,
  }
  console.log('login action:', action)
  return action;
}

Reducer:

import {
  LOGIN,
  LOGOUT,
} from '../constants/constants';

const login = (action) => {
  console.log('hitting B')
  const { auth, token, password, username } = action;

  return {
    auth: auth,
    token: token,
    password: password,
    username: username,
  }
}

const authControl = (state = [], action) => {
  console.log('hitting C: ', action)
  let authControl = null;
  switch(action.type) {
    case LOGIN:
      authControl = [...state, login(action)]
      console.log('authControl:'. authControl);
      return authControl;
    default:
      console.log('hittibbng default', state)
      return state;
  }
}

export default authControl;
Daniel
  • 155
  • 2
  • 12
  • There could be a series of possibilities: #1 check that `import reducer from './reducers/usersRedcuers';` is actually importing `authControl`. #2) Check that you are not mutating any of the state (Have you tried setting breakpoints in the reducer to confirm that it's not reaching it at all?) – Jose A Dec 15 '18 at 17:24

2 Answers2

5

In the App.jsx component you should use the action passed as a prop to the component and not call the action directly.

The loginHandler should look like this:

  loginHandler() {
    const { password, username } = this.state;
    const auth = Auth.isUserAuthenticated()
    const token = null;

    this.props.login(auth, token, password, username);   
  };
kakamg0
  • 1,096
  • 5
  • 12
  • This did the trick, thanks! Just curious, if you import the function from the Actions file, why would it need to be called as a prop? – Daniel Dec 16 '18 at 17:59
  • 1
    The action needs to be dispatched for redux to know it has to change the store. When you call the function from the actions file you're just creating the object that represents the action, but redux is unaware that the store needs to be changed. I think [this](https://stackoverflow.com/a/34458710) and [this](https://redux.js.org/api/bindactioncreators) will help you undestand it better. – kakamg0 Dec 17 '18 at 06:16
  • Thanks! I was missing this.props when working with an old class component, probably forgot about "this" because of moving to hooks. – Hisham Mubarak Sep 26 '22 at 19:04
0

Seems like you have missed to dispatch to the reducer

import {
  ADD_USER,
  DELETE_USER,
  LOGIN,
  LOGOUT,
  UPDATE_USER,
} from '../constants/constants';

export const login = (auth, token, password, username) => dispatch => {
  const action = {
    type: LOGIN,
    auth,
    token,
    password,
    username,
  }
  console.log('login action:', action)
  dispatch(action)
}
Aamin Khan
  • 712
  • 4
  • 12