hwe
hwe

Reputation: 227

Redux Saga not calling actions on click event

I am using Redux Saga to handle async requests. I have tried to go onto to other posts but there were not of help for me

The issue

I have created a route in my file called IndexRoute, my issue is that when it loads the page, it automatically loads my saga function, when it should only be called when the user clicks on a button calling an action creator. Also, when I call an action inside of my index.js, an action is called, however, the saga function is not called. I am very confused, any help will be appreciated thank you

route.js

import { getAsyncInjectors } from 'utils/asyncInjectors';
import globalSagas from 'containers/App/sagas';
import App from 'containers/App';

const errorLoading = (err) => {
  console.error('Dynamic page loading failed', err); // eslint-disable-line no-console
};

const loadModule = (cb) => (componentModule) => {
  cb(null, componentModule.default);
};

export function createRoutes(store, auth) {
  // create reusable async injectors using getAsyncInjectors factory
  const { injectReducer, injectSagas } = getAsyncInjectors(store);


 // injectReducer('global', globalReducer);
  injectSagas(globalSagas);

  const routes = [
    {
      path: '/',
      name: 'main',
      getComponent(nextState, cb) {
        const importModules = Promise.all([
          System.import('containers/Main/actions'),
          System.import('containers/Main/reducer'),
          System.import('containers/Main/sagas'),
          System.import('containers/Main'),
        ]);

        const renderRoute = loadModule(cb);

        importModules.then(([actions, reducer, sagas, component]) => {
          injectReducer('main', reducer.default);
          injectSagas(sagas.default);

          renderRoute(component);
        });

        importModules.catch(errorLoading);
      },
      indexRoute: {
        path:'/',
        name:'posts',
        getComponent(nextState, cb) {
          const importModules = Promise.all([
            System.import('containers/Dashboard/actions'),
            System.import('containers/Dashboard/reducer'),
            System.import('containers/Dashboard/sagas'),
            System.import('containers/Dashboard'),

          ]);

          const renderRoute = loadModule(cb);

          importModules.then(([actions, reducer, sagas, component]) => {
            injectReducer('posts', reducer.default);
            injectSagas(sagas.default);

            renderRoute(component);
          });

          importModules.catch(errorLoading);
        },
      },
      childRoutes: [
        {
          path: '/reports',
          name: 'Reports List',
          getComponent(nextState, cb) {
            const importModules = Promise.all([
              System.import('containers/Reports/reducer'),
              System.import('containers/Reports/sagas'),
              System.import('containers/Reports'),
            ]);

            const renderRoute = loadModule(cb);

            importModules.then(([reducer, sagas, component]) => {
              injectReducer('reportsList', reducer.default);
              injectSagas(sagas.default);
              renderRoute(component);
            });

            importModules.catch(errorLoading);
          },
          childRoutes: [
            {
              path: '/reports/new',
              name: 'Create a Report',
              getComponent(nextState, cb) {
                const importModules = Promise.all([
                  System.import('containers/Reports/CreateReport'),
                ]);

                const renderRoute = loadModule(cb);

                importModules.then(([component]) => {
                  renderRoute(component);
                });

                importModules.catch(errorLoading);
              },
            },
          ],
        },
        {
          path: 'matrixView(/:reportId)',
          name: 'Matrix View',
          getComponent(nextState, cb) {
            const importModules = Promise.all([
              System.import('containers/MatrixView/reducer'),
              System.import('containers/MatrixView/sagas'),
              System.import('containers/MatrixView'),
            ]);

            const renderRoute = loadModule(cb);

            importModules.then(([reducer, sagas, component]) => {
              injectReducer('matrixView', reducer.default);
              injectSagas(sagas.default);
              renderRoute(component);
            });

            importModules.catch(errorLoading);
          },
        },
        {
          path: '/forbidden',
          name: 'No Access',
          getComponent(nextState, cb) {
            const importModules = Promise.all([
              System.import('containers/NoAccess'),
            ]);

            const renderRoute = loadModule(cb);

            importModules.then(([component]) => {
              renderRoute(component);
            });

            importModules.catch(errorLoading);
          },
        },
      ],
    },
    {
      path: '/login',
      name: 'login',
      getComponent(nextState, cb) {
        const importModules = Promise.all([
          System.import('containers/Login'),
        ]);

        const renderRoute = loadModule(cb);

        importModules.then(([component]) => {
          renderRoute(component);
        });

        importModules.catch(errorLoading);
      },
    },
    {
      path: '/signup',
      name: 'signup',
      getComponent(nextState, cb) {
        const importModules = Promise.all([
          System.import('containers/Signup'),
        ]);

        const renderRoute = loadModule(cb);

        importModules.then(([component]) => {
        //  injectReducer('signup', reducer.default);
        //  injectSagas(sagas.default);

          renderRoute(component);
        });

        importModules.catch(errorLoading);
      },
    },
  ];

  return {
    component: App,
   // path: '/',
  //  indexRoute: { onEnter: (nextState, replace) => replace('/account/me') },
    childRoutes: routes,
  };
}

actions.js

import {
  CREATE_MATRIX_REQUEST,
  CREATE_MATRIX_SUCCESS,
  CREATE_MATRIX_ERROR
} from './constants';



export function createMatrixRequest() {
  return { type: CREATE_MATRIX_REQUEST};
}

export function createMatrixSuccess(data) {
  return { type: CREATE_MATRIX_SUCCESS, data };
}

export function createMatrixError(error) {
  return { type: CREATE_MATRIX_ERROR , error };
}

reducer.js

/*
 * The reducer takes care of state changes in our app through actions
 */
import { fromJS } from 'immutable';
import {
    CREATE_MATRIX_REQUEST,
    CREATE_MATRIX_SUCCESS,
    CREATE_MATRIX_ERROR
} from './constants';

// The initial application state
 const initialState = fromJS({
   success: '',
   error: ''
 });

// Takes care of changing the application state
function createMatrixReducer(state = initialState, action) {
  switch (action.type) {
    case CREATE_MATRIX_REQUEST:
    console.log('hello')
     return state;
    case CREATE_MATRIX_SUCCESS:
    console.log('second hello')
     return state.set('success', action.payload);
    case CREATE_MATRIX_ERROR:
      return state.set('error', action.payload);
    default:

      return state;
  }
}

export default createMatrixReducer;

sagas.js

import { call, put } from 'redux-saga/effects';
import { createMatrix } from './utils';

import { CREATE_MATRIX_REQUEST, CREATE_MATRIX_SUCCESS, CREATE_MATRIX_ERROR } from './constants';

export function* createMatrixSaga(action) {
  console.log('Generator called')
  yield put({ type:CREATE_MATRIX_REQUEST});
   try {
      const data = yield call(createMatrix);
      yield put({type: CREATE_MATRIX_SUCCESS, success: data})
   } catch (error) {
      yield put({type: CREATE_MATRIX_ERROR, error: error })
   }
}
export default [
  createMatrixSaga,
];

index.js

/*
 * Dashboard
 *
 **/

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { Input, Button } from 'muicss/react';
import { Link } from 'react-router';
import { createMatrixRequest } from './actions';
import { UserIsAuthenticated } from 'config.routes/UserIsAuthenticated';

import styles from './styles.scss';

class Dashboard extends Component {
  constructor(props) {
    super(props);

    this.state = {
      domain: '',
    };

    this.inputChange = this.inputChange.bind(this);
    this.clearInput = this.clearInput.bind(this);
    this.createMatrix = this.createMatrix.bind(this);
  }

  inputChange(event) {
    const name = event.target.name;
    const value = event.target.value;
    this.setState({
      [name]: value,
    });
  }

  clearInput(){
    this.setState({
      domain: ''
    })
  }

  createMatrix(){
    this.props.createMatrixRequest();
  }

  render() {
    console.log(this.props, 'This are the props')
    return (
      <div className={styles.dashboardContainer}>
        <div className={styles.dashboardBody}>
          <h1>Let's Get Started</h1>
          <h5>Begin by entering a domain</h5>
          <Input
            className={styles.domainInput}
            label="Domain Name"
            type="text"
            name="domain"
            value={this.state.domain}
            floatingLabel="true"
            onChange={this.inputChange}
            required
          />
          <Button
            variant="raised"
            type="button"
            onClick={this.createMatrix}
            disabled={this.state.domain.length === 0}
          >

          </Button>

          <h5 onClick={this.clearInput}><Link>Clear</Link> input</h5>
        </div>
      </div>
    );
  }
}

export function mapDispatchToProps(dispatch) {
  return {
    createMatrixRequest: () => dispatch(createMatrixRequest()),
  };
}


function mapStateToProps(state){
  return { matrix: state };
}



export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(Dashboard));

I also tried adding it to the global sagas file that I import routes.js, but it didn't help This filed is named the same as my above file, but it stays in another folder called App

sagas.js

// This file contains the sagas used for async actions in our app. It's divided into
// "effects" that the sagas call (`authorize` and `logout`) and the actual sagas themselves,
// which listen for actions.

// Sagas help us gather all our side effects (network requests in this case) in one place

import { take, call, put, race } from 'redux-saga/effects';
import { browserHistory } from 'react-router';

import auth from 'utils/auth';

import { toastr } from 'lib/react-redux-toastr';

import {
  SENDING_REQUEST,
  LOGIN_REQUEST,
  REGISTER_REQUEST,
  SET_AUTH,
  LOGOUT,
  FETCH_USER,
  CHANGE_FORM,
  REQUEST_ERROR,
  SET_USER,
  CLEAR_USER,
  CREATE_MATRIX_REQUEST,
  CREATE_MATRIX_ERROR,
  CREATE_MATRIX_SUCCESS
} from './constants';

/**
 * Effect to handle authorization
 * @param  {string} email                  The email of the user
 * @param  {string} password               The password of the user
 * @param  {object} options                Options
 * @param  {boolean} options.isRegistering Is this a register request?
 */
export function* authorize({ name, email, password, accountType, isRegistering }) {
  // We send an action that tells Redux we're sending a request
  yield put({ type: SENDING_REQUEST, sending: true });

  // We then try to register or log in the user, depending on the request
  try {
   // let salt = genSalt(email);
   // let hash = hashSync(password, salt);
    let response;

    // For either log in or registering, we call the proper function in the `auth`
    // module, which is asynchronous. Because we're using generators, we can work
    // as if it's synchronous because we pause execution until the call is done
    // with `yield`!
    if (isRegistering) {
      response = yield call(auth.register, name, email, password, accountType);
    } else {
      response = yield call(auth.login, email, password);
    }
    return response;
  } catch (error) {
    console.log('hi');
    // If we get an error we send Redux the appropiate action and return
    yield put({ type: REQUEST_ERROR, error: error.message });

    return false;
  } finally {
    // When done, we tell Redux we're not in the middle of a request any more
    yield put({ type: SENDING_REQUEST, sending: false });
  }
}

/**
 * Effect to handle logging out
 */
export function* logout() {
  // We tell Redux we're in the middle of a request
  yield put({ type: SENDING_REQUEST, sending: true });

  // Similar to above, we try to log out by calling the `logout` function in the
  // `auth` module. If we get an error, we send an appropiate action. If we don't,
  // we return the response.
  try {
    const response = yield call(auth.logout);
    yield put({ type: SENDING_REQUEST, sending: false });
    return response;
  } catch (error) {
    yield put({ type: REQUEST_ERROR, error: error.message });
    return error.message;
  }
}

/**
 * Log in saga
 */
export function* loginFlow() {
  // Because sagas are generators, doing `while (true)` doesn't block our program
  // Basically here we say "this saga is always listening for actions"
  while (true) {
    // And we're listening for `LOGIN_REQUEST` actions and destructuring its payload
    const request = yield take(LOGIN_REQUEST);
    const { email, password } = request.data;

    // A `LOGOUT` action may happen while the `authorize` effect is going on, which may
    // lead to a race condition. This is unlikely, but just in case, we call `race` which
    // returns the "winner", i.e. the one that finished first
    const winner = yield race({
      auth: call(authorize, { email, password, isRegistering: false }),
      logout: take(LOGOUT),
    });
    // If `authorize` was the winner...
    if (winner.auth) {
      // ...we send Redux appropiate actions
      yield put({ type: SET_AUTH, newAuthState: true }); // User is logged in (authorized)
      yield put({ type: SET_USER, user: winner.auth });
      yield put({ type: CHANGE_FORM, newFormState: { email: '', password: '' } }); // Clear form
      yield call(forwardTo, '/'); // Go to dashboard page
      // If `logout` won...
    } else if (winner.logout) {
      // ...we send Redux appropiate action
      yield put({ type: SET_AUTH, newAuthState: false }); // User is not logged in (not authorized)
      yield call(logout); // Call `logout` effect
      yield call(forwardTo, '/login'); // Go to root page
    }
  }
}

/**
 * Log out saga
 * This is basically the same as the `if (winner.logout)` of above, just written
 * as a saga that is always listening to `LOGOUT` actions
 */
export function* logoutFlow() {
  while (true) {
    yield take(LOGOUT);
    yield put({ type: SET_AUTH, newAuthState: false });

    yield call(logout);
    yield put({ type: CLEAR_USER });

    yield call(forwardTo, '/login');

    toastr.success('Success!', 'You are now logged out.');
  }
}

/**
 * Get user information saga
 */
export function* getUserFlow() {
  while (true) {
    yield take(FETCH_USER);
    try {
      const response = yield call(auth.getUserInfo);
      yield put({ type: SET_USER, user: response });
    } catch (error) {
      yield put({ type: REQUEST_ERROR, error: error.message });
      return error.message;
    }
  }
}

/**
 * Register saga
 * Very similar to log in saga!
 */
export function* registerFlow() {
  while (true) {
    // We always listen to `REGISTER_REQUEST` actions
    const request = yield take(REGISTER_REQUEST);
    const { name, email, password, accountType } = request.data;
    // We call the `authorize` task with the data, telling it that we are registering a user
    // This returns `true` if the registering was successful, `false` if not
    const wasSuccessful = yield call(authorize, {name, email, password, accountType, isRegistering: true });

    // If we could register a user, we send the appropiate actions
    if (wasSuccessful) {
      yield put({ type: SET_AUTH, newAuthState: true }); // User is logged in (authorized) after being registered
      yield put({ type: CHANGE_FORM, newFormState: { name: '', password: '' } }); // Clear form
      yield put({ type: LOGIN_REQUEST, data: { email, password } });
      forwardTo('/dashboard'); // Go to dashboard page
    }
  }
}

// The root saga is what we actually send to Redux's middleware. In here we fork
// each saga so that they are all "active" and listening.
// Sagas are fired once at the start of an app and can be thought of as processes running
// in the background, watching actions dispatched to the store.
export default [
  loginFlow,
  logoutFlow,
  registerFlow,
  getUserFlow,
];

// Little helper function to abstract going to different pages
export function* forwardTo(location) {
  yield call(browserHistory.push, location);
}

Upvotes: 0

Views: 2048

Answers (1)

hwe
hwe

Reputation: 227

Found an answer, basically I forgot to add a watcher to my saga file, now it works perfectly!

import { call, put } from 'redux-saga/effects';
import { takeEvery } from 'redux-saga/effects'
import { createMatrix } from './utils';

import { CREATE_MATRIX_REQUEST, CREATE_MATRIX_SUCCESS, CREATE_MATRIX_ERROR } from './constants';

export function* createMatrixSaga(action) {

   try {
      const data = yield call(createMatrix);
      yield put({type: CREATE_MATRIX_SUCCESS, success: data})
   } catch (error) {
      yield put({type: CREATE_MATRIX_ERROR, error: error })
   }
}


function* watchFetchData() {
  yield takeEvery(CREATE_MATRIX_REQUEST, createMatrixSaga)
}


export default [
  watchFetchData,
];

Upvotes: 0

Related Questions