cbutler
cbutler

Reputation: 943

Redux-saga not picking up action dispatch

I am using redux-saga in a new create-react-app react project with redux and for some reason it seems redux-saga is not picking up the dispatched action. A console log statement in the reducer shows the action does reach the reducer. The odd thing about all this is that I've based this project off other projects where I have successfully used redux-saga (but are not create-react-app's). I'm wondering if it is a versioning issue or some support package is required that I am missing. Below is a full code snippet.

From my package.json file:

  "dependencies": {
    "axios": "^0.19.0",
    "connect-history-api-fallback": "^1.6.0",
    "connected-react-router": "^6.5.2",
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-google-login": "^5.0.5",
    "react-redux": "^7.1.1",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.1.1",
    "redux": "^4.0.4",
    "redux-actions": "^2.6.5",
    "redux-immutable-state-invariant": "^2.1.0",
    "redux-persist": "^5.10.0",
    "redux-saga": "^1.0.5",
    "shortid": "^2.2.14",
    "styled-components": "^4.3.2",
    "styled-modern-normalize": "^0.2.0"
  },
  "devDependencies": {
    "react-hot-loader": "^4.12.11",
    "history": "^4.9.0"
  },

the outer most index.js file (entry point for my app):

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './components/App';
import store from './store/config';

const render = (Component) => {
  ReactDOM.render(
    <Provider store={store}>
      <Component />
    </Provider>,
    document.querySelector('#root')
  );
};

render(App);

the app.js file is basically a container component with a connected router from connected-react-router which has the app header and then a switch with the app routes. I'm not showing it because it is actually a few nested components and it may just confuse.

I create my store like this:

import {
  applyMiddleware,
  compose,
  createStore,
} from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from '../root/rootReducer';
import sagas from '../root/rootSaga';

const sagaMiddleware = createSagaMiddleware();
const initialState = undefined;

const store = createStore(
  rootReducer(history),
  initialState,
  applyMiddleware(sagaMiddleware),
);

sagaMiddleware.run(sagas);
export default store;

Next is the root reducer:

import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import authReducer from '../auth/authReducer';
import { stateKeys } from '../../types';

export default history => combineReducers({
  [stateKeys.ROUTER]: connectRouter(history),
  [stateKeys.AUTH]: authReducer,
});

and root saga:

import { 
  watchRegister,
  watchSignin,
  watchSignout,
} from '../auth/authSaga';

const root = function* rootSaga() {
  yield [
    watchRegister(),
    watchSignin(),
    watchSignout(),
  ];
};

export default root;

My auth Saga:

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import api from '../../services/api';
import * as actions from '../root/rootActions';

function* signInSaga({ payload }) {
  try {
    console.log('saga hit...');
    // console.log('email: ', email);
    // console.log('password: ', password);
    // yield put(actions.signinRequested());
    // const response = yield call(api.signin, { email, password });
    // console.log('response: ', response);
    // yield put(actions.signinSucceeded(response));
  } catch (error) {
    console.log('saga failed...');
    yield put(actions.signinFailed());
  }
}

export function* watchSignin() {
  yield takeEvery(actions.signin.toString(), signInSaga);
}

auth actions:

import { createAction } from 'redux-actions';
export const signin = createAction('auth/signin');
export const signinRequested = createAction('auth/signin_requested');
export const signinSucceeded = createAction('auth_signin_succeeded');
export const signinFailed = createAction('auth_signin_failed');

finally the signin component:

import React, { Component, Fragment} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link, Redirect } from 'react-router-dom';
import * as actions from '../../store/root/rootActions';
import * as selectors from '../../store/root/rootSelectors';

class Signin extends Component {
...

  onSubmitClick = (event) => {
    event.preventDefault();

    this.setState({ loading: true });

    const { email, password } = this.state;
    const user = {
      email,
      password
    };
    // dispatching the action
    this.props.signin(user);
  };

  renderSigninForm() {
    const { email, password } = this.state;
    return (
      <form className="ui large form">
        <div className="ui stacked segment">
          <div className="field">
            <div className="ui left icon input">
              <i className="user icon"></i>
              <input onChange={this.handleChange("email")}  type="email" placeholder="[email protected]" value={email} />
            </div>
          </div>
          <div className="field">
            <div className="ui left icon input">
              <i className="lock icon"></i>
              <input onChange={this.handleChange("password")}  type="password" placeholder="Password" value={password} />
            </div>
          </div>
        <div onClick={this.onSubmitClick} className="ui fluid large primary submit button">Log in</div>
        </div>
      </form>
    );
  }

  render() {
    if (this.state.redirectToReferer) {
      return <Redirect to="/" />;
    }

    return (
      <div className="ui middle aligned center aligned grid">
        <div className="column">
          <h2 className="content">Signin</h2>
          {this.renderSigninForm()}
        </div>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  auth: selectors.getAuth(state),
});

const mapDispatchToProps = dispatch => ({
  signin: user => dispatch(actions.signin(user)),
});

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

I apologize for the epic length of this post but it's hard to provide a complete code snippet without everything. Any suggestions are welcome :) Thanks in advance

Upvotes: 3

Views: 6742

Answers (1)

bragamat
bragamat

Reputation: 101

Seems that your root saga file is not calling a saga method to start your sagas. As the documentation says: you should call all() method to run your saga setup. (e.g https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html).

so your refactored rootSaga.js would look like this:

import { all } from 'redux-saga/effects'
import { 
  watchRegister,
  watchSignin,
  watchSignout,
} from '../auth/authSaga';

const root = function* rootSaga() {
  yield all([
    watchRegister(),
    watchSignin(),
    watchSignout(),
  ]);
};

export default root;

Upvotes: 5

Related Questions