未调用 Redux saga 函数
Redux saga function is not being called
我尝试在我的 React 项目中使用 redux-saga,但是没有调用 redux-saga 函数。 access.actions.js
中的 toggleLoginModal
操作被调用,但 access.sagas.js
中的 toggleLoginModal
未被调用。
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"
},
有两个问题。
首先,您需要使用 props 中的 toggleLoginModal
- 由于 connect
,它被包裹在 dispatch
中。引用 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.
所以在Header.jsx中使用:
handleLoginClick = () => {
this.props.toggleLoginModal(true);
};
第二个问题是您使用 toggleLoginModalReturn
作为 thunk 而没有 thunk 中间件。将 redux-thunk
添加到您的中间件列表。
只是把它留在这里因为它可能对某人有帮助。我在为一个项目配置 sagas 时遇到了一个问题,即使一切都已配置和设置正确, saga 也不会触发。控制台也没有抛出任何错误。但实际问题是,在定义状态的默认值时,我应该为对象提供默认值,我只是传递了 null 而不是使用属性构造整个对象(懒惰的我)。
所以这实际上会导致错误,如果您尝试从这些对象访问属性,但将初始值作为 null 传递,您将尝试从 null 访问属性 - 这会弄乱项目。在某些情况下,它会抛出错误,而在某些情况下则不会。所以这也是传奇无法正常工作的原因。
如果有人觉得它有用,请将其删除。
我使用 react-redux
中的 connect
来绑定 mapStateToProps
和 mapDispatchToProps
.
出于某种原因,当我将 mapDispatchToProps
用作函数时,分派的操作永远无法到达 saga 侦听器。我所要做的就是将变量从函数转移到一个简单的对象而不是。
来自
const mapDispatchToProps = (dispatch) => ({
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
});
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
收件人:
const mapStateToProps = (state) => ({
userSettings: selectUserSettings(state),
userSettingsStatus: selectUserSettingsStatus(state),
});
const mapDispatchToProps = {
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
};
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
导出动作文件时:
export const getUserSettings = () => ({type: actions.FETCH_USER_SETTINGS});
export const saveUserSettings = (settings) => ({type: actions.SAVE_USER_SETTINGS, settings});
我意识到没有必要为 mapDispatchToProps 定义函数,因为我没有在函数上使用调度参数。此外,我返回的对象的每个字段都直接使用动作创建者,在这种情况下,我们可以 Object Shorthand form 其中:
React-Redux binds the dispatch of your store to each of the action
creators using bindActionCreators.
Read more 关于将 mapDispatchToProps
定义为对象。
我尝试在我的 React 项目中使用 redux-saga,但是没有调用 redux-saga 函数。 access.actions.js
中的 toggleLoginModal
操作被调用,但 access.sagas.js
中的 toggleLoginModal
未被调用。
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"
},
有两个问题。
首先,您需要使用 props 中的 toggleLoginModal
- 由于 connect
,它被包裹在 dispatch
中。引用 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.
所以在Header.jsx中使用:
handleLoginClick = () => {
this.props.toggleLoginModal(true);
};
第二个问题是您使用 toggleLoginModalReturn
作为 thunk 而没有 thunk 中间件。将 redux-thunk
添加到您的中间件列表。
只是把它留在这里因为它可能对某人有帮助。我在为一个项目配置 sagas 时遇到了一个问题,即使一切都已配置和设置正确, saga 也不会触发。控制台也没有抛出任何错误。但实际问题是,在定义状态的默认值时,我应该为对象提供默认值,我只是传递了 null 而不是使用属性构造整个对象(懒惰的我)。
所以这实际上会导致错误,如果您尝试从这些对象访问属性,但将初始值作为 null 传递,您将尝试从 null 访问属性 - 这会弄乱项目。在某些情况下,它会抛出错误,而在某些情况下则不会。所以这也是传奇无法正常工作的原因。
如果有人觉得它有用,请将其删除。
我使用 react-redux
中的 connect
来绑定 mapStateToProps
和 mapDispatchToProps
.
出于某种原因,当我将 mapDispatchToProps
用作函数时,分派的操作永远无法到达 saga 侦听器。我所要做的就是将变量从函数转移到一个简单的对象而不是。
来自
const mapDispatchToProps = (dispatch) => ({
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
});
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
收件人:
const mapStateToProps = (state) => ({
userSettings: selectUserSettings(state),
userSettingsStatus: selectUserSettingsStatus(state),
});
const mapDispatchToProps = {
onChange: (settings) => saveUserSettings(settings),
fetchDataOnMount: getUserSettings,
};
export default connect(mapStateToProps, mapDispatchToProps)(Settings);
导出动作文件时:
export const getUserSettings = () => ({type: actions.FETCH_USER_SETTINGS});
export const saveUserSettings = (settings) => ({type: actions.SAVE_USER_SETTINGS, settings});
我意识到没有必要为 mapDispatchToProps 定义函数,因为我没有在函数上使用调度参数。此外,我返回的对象的每个字段都直接使用动作创建者,在这种情况下,我们可以 Object Shorthand form 其中:
React-Redux binds the dispatch of your store to each of the action creators using bindActionCreators.
Read more 关于将 mapDispatchToProps
定义为对象。