我正在尝试在一个异步动作创建者中调度另一个动作。但是,当我使用redux-thunk中间件提供的调度功能时,会抛出Uncaught TypeError: dispatch is not a function
错误。
以下是更多帮助的文件
store / store.dev.js
use strict';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducers';
import ReduxThunk from 'redux-thunk';
export default function configureStore(initialState = {}) {
const store = createStore(rootReducer, initialState, applyMiddleware(ReduxThunk));
console.log('store returned', store);
return store;
}
src / index.js
'use strict';
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import configureStore from './store';
import App from './containers/App.js';
import routes from './routes/index.js';
hydrate(
<Provider store={configureStore()}>
<BrowserRouter>
{renderRoutes(routes)}
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
减速器
'use strict';
import { LOGIN_SUBMITTED, LOGIN_COMPLETED } from '../constants/ActionTypes.js';
const loginSubmitReducer = (state = [], action) => {
console.log('in reducer');
switch (action.type) {
case LOGIN_SUBMITTED:
console.log('login submitted case');
return Object.assign({}, state, {
loginSubmitted: true
});
case LOGIN_COMPLETED:
console.log('login completed case');
return Object.assign({}, state, {
loginCompleted: true
});
default:
console.log('in default case');
return {
a:1
};
}
}
export default loginSubmitReducer;
容器/App.js
'use strict';
import React from 'react';
import { loginCompleted, loginSubmitted } from '../actions';
import { createStructuredSelector, createSelector } from 'reselect';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Header, ButtonDemo, LoginForm } from '../components';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
class App extends React.Component {
constructor(props, context) {
super(props);
this.state = {
appState: {
entry: 'yes'
}
}
console.log('props in App constructor', props);
console.log('context in App constructor', context);
this.newFunc = this.newFunc.bind(this);
}
newFunc () {
console.log('props', this.props);
}
render() {
console.log('props in main App', this.props);
console.log('Actions', this.props.actions);
console.log('context in App', this.context);
// this.props.actions.loginCompleted();
const muiTheme = getMuiTheme({
lightBaseTheme
});
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div>
<Header />
<LoginForm
appstate = {this.props.appState}
dispatchAction = {this.props.actions}
/>
</div>
</MuiThemeProvider>
);
}
}
// const mapStateToProps = createStructuredSelector({});
const mapStateToProps = (state) => {
return { appState: state.loginSubmitReducer };
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ loginCompleted, loginSubmitted }, dispatch)
};
// dispatch(loginCompleted());
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
组件
'use strict';
import React, { PropTypes } from 'react';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ContentAdd from 'material-ui/svg-icons/content/add';
import { loginAction } from '../actions';
// import '../css/style.css';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.loginSubmit = this.loginSubmit.bind(this);
}
loginSubmit() {
console.log('Login fomr submitted', this.props);
console.log('loginAction', loginAction);
loginAction()();
}
render () {
console.log('props when login rendered', this.props);
return (
<div className="loginForm">
<div className="title">LOGIN</div>
<form>
<TextField
hintText="Username"
floatingLabelText="Username"/><br/>
<br/>
<TextField
hintText="Password"
type="Password"
floatingLabelText="Password"/><br/>
<br/>
<RaisedButton
label="Login"
secondary={true}
onClick={this.loginSubmit}
className="submitBtn" /><br/>
<br/>
<FloatingActionButton
secondary={true}
className="registerBtn">
<ContentAdd />
</FloatingActionButton>
</form>
</div>
)
}
}
export default LoginForm;
动作/登录Action.js
'use strict';
// import { polyfill } from 'es6-promise';
// require('es6-promise').polyfill();
// require('isomorphic-fetch');
import request from 'superagent';
// import fetch from 'isomorphic-fetch';
import loginCompleted from './loginCompletedAction.js';
import loginSubmitted from './loginSubmittedAction.js';
const loginAction = (dispatch) => {
console.log('validateUserLogin executed', this);
console.log('validateUserLogin dispatch', dispatch);
return (dispatch) => {
// console.log('first return', dispatch);
// this.props.dispatch(loginSubmitted());
// this.props.dispatchAction.loginSubmitted();
dispatch(loginSubmitted());
request.get('http://localhost:3000/loginSubmit')
.end((err, res) => {
console.log('res', res);
console.log('err', err);
// this.props.dispatchAction.loginCompleted();
});
}
}
export default loginAction;
资料夹结构
我正在使用
redux-thunk
中间件,该中间件将分派功能公开给异步操作创建者。仍然不起作用。请帮忙!
最佳答案
动作创建者的第一个参数不应为dispatch
,而应传递给动作创建者的参数。
const loginAction = () => (dispatch) => {
console.log('validateUserLogin executed', this);
console.log('validateUserLogin dispatch', dispatch);
// console.log('first return', dispatch);
// this.props.dispatch(loginSubmitted());
// this.props.dispatchAction.loginSubmitted();
dispatch(loginSubmitted());
request.get('http://localhost:3000/loginSubmit')
.end((err, res) => {
console.log('res', res);
console.log('err', err);
// this.props.dispatchAction.loginCompleted();
});
}
编辑
作为您的评论的后续部分,首先,我认为您没有将
redux-thunk
正确地实现为中间件,但是根据您的代码,它看起来还可以。然后我注意到您并不是真正在调度
loginAction
而是在调用它。您应该将其发送
dispatch(loginAction())
。您的组件应连接到redux
,然后您将可以访问dispatch
。