Reputation: 8942
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"
},
Upvotes: 3
Views: 10629
Reputation: 9282
Just dropping this in case someone finds it useful.
I was using connect
from react-redux
to bind mapStateToProps
and mapDispatchToProps
.
For some reason when I was using mapDispatchToProps
as a function, the actions dispatched was never able to reach the saga listener. All I had to do was shift the variable from the function to a simple object instead.
FROM
const mapDispatchToProps = (dispatch) => ({
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
});
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
TO :
const mapStateToProps = (state) => ({
userSettings: selectUserSettings(state),
userSettingsStatus: selectUserSettingsStatus(state),
});
const mapDispatchToProps = {
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
};
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
while my action file exporting:
export const getUserSettings = () => ({type: actions.FETCH_USER_SETTINGS});
export const saveUserSettings = (settings) => ({type: actions.SAVE_USER_SETTINGS, settings});
I realized there was no necessity for having a function defined for mapDispatchToProps as I was not using the dispatch parameter on the function. Moreover, each field of my returning object was utilizing the action creators directly and in this case, we can Object Shorthand form where:
React-Redux binds the dispatch of your store to each of the action creators using bindActionCreators.
Read more about defining mapDispatchToProps
as an object.
Upvotes: 0
Reputation: 301
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.
Upvotes: 1
Reputation: 4975
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.
Upvotes: 3