I tried to use redux-saga in my React project, but redux-saga function is not being called. toggleLoginModal action in access.actions.js is called, but toggleLoginModal in access.sagas.js is not called.
Header.jsx
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import accessActions from 'actions/access.actions';
import { connect } from 'react-redux';
import './Header.scss';
const { toggleLoginModal, toggleRegisterModal } = accessActions;
class Header extends Component {
handleLoginClick = () => {
toggleLoginModal(true);
};
render() {
return (
<div className="navbar-right">
<button type="button" className="btn btn-link" onClick={this.handleLoginClick}>Log in</button>
<button type="button" className="btn btn-primary">Register</button>
</div>
);
}
}
export default withRouter(connect(null, { toggleLoginModal, toggleRegisterModal })(Header));
access.action.js
const actions = {
TOGGLE_LOGIN_MODAL: 'TOGGLE_LOGIN_MODAL',
TOGGLE_LOGIN_MODAL_RETURN: 'TOGGLE_LOGIN_MODAL_RETURN',
toggleLoginModal: newState => ({
type: actions.TOGGLE_LOGIN_MODAL,
state: newState,
}),
toggleLoginModalReturn: (newState) => {
return (dispatch) => {
dispatch({
type: actions.TOGGLE_LOGIN_MODAL_RETURN,
newState,
});
};
},
};
export default actions;
access.sagas.js
import { all, takeEvery, put } from 'redux-saga/effects';
import actions from 'actions/access.actions';
export function* toggleLoginModal({ state }) {
yield put(actions.toggleLoginModalReturn(state));
}
export default function* rootSaga() {
yield all([
takeEvery(actions.TOGGLE_LOGIN_MODAL, toggleLoginModal),
]);
}
access.reducers.js
export function toggleModals(state = { login: false, register: false }, action) {
switch (action.type) {
case 'TOGGLE_LOGIN_MODAL_RETURN':
return { login: action.newState };
default:
return state;
}
}
reducers.js
import { toggleModals } from 'state/access.reducers.js';
export default {
toggleModals,
};
sagas.js
import { all } from 'redux-saga/effects';
import accessSagas from 'sagas/access.sagas';
export default function* rootSaga() {
yield all([accessSagas()]);
}
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import createHistory from 'history/createBrowserHistory';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import { Provider } from 'react-redux';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import App from 'App';
import reducers from 'reducers';
import sagas from 'sagas';
const history = createHistory();
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combineReducers({
...reducers,
router: routerReducer,
}),
composeWithDevTools(applyMiddleware(
routerMiddleware(history),
sagaMiddleware,
)),
);
sagaMiddleware.run(sagas);
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('app'),
);
package.json
"dependencies": {
"bootstrap": "^4.1.1",
"jquery": "^3.3.1",
"js-cookie": "^2.2.0",
"lodash": "^4.17.10",
"moment": "^2.22.1",
"popper.js": "^1.14.3",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-loadable": "^5.4.0",
"react-modal": "^3.4.4",
"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"react-tippy": "^1.2.2",
"react-toastify": "^4.0.1",
"redux": "^4.0.0",
"redux-saga": "^0.16.0"
},
There are two problems.
First you need to use toggleLoginModal from props - as it is wrapped in dispatch thanks to connect. To cite https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props.
So in Header.jsx use:
handleLoginClick = () => {
this.props.toggleLoginModal(true);
};
Second problem is that you use toggleLoginModalReturn as a thunk without having thunk middleware. Add redux-thunk to your list of middlewares.
Just leaving this here because it might help someone. I came across an issue when configuring sagas for a project where the saga would not trigger even though everything was configured and set up just right. The console didn't throw any errors either. But the actual problem was that when defining the default values for the state, where I was supposed to give default values for objects, I just passed null instead of constructing whole objects with the properties (lazy me).
So that actually causes errors where if you are trying to access properties from those objects but you pass the initial value as null, you'll be trying to access properties from null - which would mess up the project. And in some cases, it throws errors and some cases it doesn't. So this was the reason that the saga wasn't working correctly either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With