redux-thunk:错误:动作必须是普通对象。使用自定义中间件进行异步操作
Posted
技术标签:
【中文标题】redux-thunk:错误:动作必须是普通对象。使用自定义中间件进行异步操作【英文标题】:redux-thunk: Error: Actions must be plain objects. Use custom middleware for async actions 【发布时间】:2019-03-04 22:54:33 【问题描述】:尝试使用 API 创建一个简单的 CRUD 应用。我已经登录和注册工作,所以我知道 redux-thunk 已在商店中正确设置。
我见过很多类似的问题,但没有一个答案能解决我的问题。我知道调度应该返回函数。我很确定情况就是这样。我是新手,对如何调试有点迷茫。 Console.log 没有显示任何内容,所以我使用了警报。我很乐意提供任何其他信息。谢谢寻找。
错误
Error: Actions must be plain objects. Use custom middleware for async actions.
20 | componentDidMount()
21 | const dispatch = this.props;
22 | const error, rosterMembers, isFetching = this.props;
23 | dispatch(rosterActions.getAll());
24 |
25 |
26 |
容器:RosterListCard.js
import React, PureComponent from 'react';
import PropTypes from 'prop-types';
import connect from 'react-redux';
import Card, CardBody, Col, ButtonToolbar from 'reactstrap';
import EmailIcon from 'mdi-react/EmailIcon'
import CheckboxMarkedCircleIcon from 'mdi-react/CheckboxMarkedCircleIcon'
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
import Link from 'react-router-dom';
import Table from 'reactstrap';
import rosterActions from '../../../../redux/actions';
class RosterListCard extends PureComponent
constructor(props)
super(props);
// this.getNoRowsRenderer = this.getNoRowsRenderer.bind(this);
// this.getRowClassName = this.getRowClassName.bind(this);
componentDidMount()
const dispatch = this.props;
const error, rosterMembers, isFetching = this.props;
dispatch(rosterActions.getAll());
componentWillReceiveProps(nextProps)
const dispatch = nextProps;
dispatch(rosterActions.getAll());
getNoRowsRenderer()
return (
<div className="noRows">
No rows
</div>
);
render()
const error, rosterMembers, isFetching = this.props;
return (
<div className="container">
console.log(error)
console.log(isFetching)
console.log(rosterMembers)
error &&
<div className="alert alert-danger">
error.message || "Unknown errors."
</div>
!isFetching &&
rosterMembers.length === 0 &&
<div className="alert alert-warning">Oops, nothing to show.</div>
rosterMembers &&
<Table striped>
<thead>
<tr>
<th>Name</th>
<th>Title</th>
</tr>
</thead>
<tbody>
this.props.rosterMembers.map((rosterMember) => (
<tr>
<th scope="row">rosterMember.fullName</th>
<td>rosterMembers.title</td>
</tr>
))
</tbody>
</Table>
</div>
)
RosterListCard.propTypes =
rosterMembers: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
dispatch: PropTypes.func.isRequired
;
function mapStateToProps (state)
return
error: null,
isFetching: false,
didInvalidate: false,
totalCount: 0,
rosterMembers: []
;
export default connect(mapStateToProps)(RosterListCard);
动作:roster.actions.js
import sessionService from 'redux-react-session';
import rosterConstants from '../constants';
import callApi from "../../utilities/api.utility.js";
export const rosterActions =
getAll
;
function rosterRequest()
return
type: rosterConstants.GETALL_REQUEST
;
function rosterSuccess()
return function(payload)
return
type: rosterConstants.GETALL_SUCCESS,
rosterMembers: payload.items,
totalCount: payload.total_count
;
;
function rosterFailure()
return function(error)
return
type: rosterConstants.GETALL_FAILURE,
error
;
;
export function getAll()
sessionService.loadSession()
.then(session =>
// if (typeof session.token === 'undefined') return rosterFailure();
const url = `$process.env.REACT_APP_API_BASE_URL/Rosters?access_token=$session.token`;
return callApi(
url,
null,
rosterRequest(),
rosterSuccess(),
rosterFailure()
)
);
API 模块:api.utility.js
import "isomorphic-fetch";
export function checkStatus(response)
if (!response.ok)
// (response.status < 200 || response.status > 300)
const error = new Error(response.statusText);
error.response = response;
throw error;
return response;
export function parseJSON(response)
return response.json();
/**
* A utility to call a restful service.
*
* @param url The restful service end point.
* @param config The config object of the call. Can be null.
* @param request The request action.
* @param onRequestSuccess The callback function to create request success action.
* The function expects response json payload as its argument.
* @param onRequestFailure The callback function to create request failure action.
* The function expects error as its argument.
*/
export function callApi(
url,
config,
request,
onRequestSuccess,
onRequestFailure
)
alert('request');
return dispatch =>
alert('request***');
dispatch(request);
return fetch(url, config)
.then(checkStatus)
.then(parseJSON)
.then(json =>
alert('request***');
dispatch(onRequestSuccess(json));
)
.catch(error =>
alert('request***');
const response = error.response;
if (response === undefined)
dispatch(onRequestFailure(error));
else
error.status = response.status;
error.statusText = response.statusText;
response.text().then(text =>
try
const json = JSON.parse(text);
error.message = json.message;
catch (ex)
error.message = text;
dispatch(onRequestFailure(error));
);
);
;
alert('request end');
【问题讨论】:
【参考方案1】:如果您使用的是redux-thunk
,那么您的异步“动作创建者”应该类似于:
export function getAll()
return dispatch =>
sessionService.loadSession()
.then(session =>
if (typeof session.token === 'undefined') return dispatch(rosterFailure());
const url = `$process.env.REACT_APP_API_BASE_URL/Rosters?access_token=$session.token`;
dispatch(rosterRequest());
fetch(url, config).then(response =>
// validate response and convert to JSON
dispatch(rosterSuccess(json));
).catch(err =>
// Handle/Transform error
dispatch(rosterFailure(err));
);
另外,我认为您遗漏了有关 redux-thunk
的一些详细信息。异步dispatch
其他操作的操作应返回函数(其中dispatch
是第一个也是唯一需要的参数)。其他动作应该只返回带有type
的典型动作对象以及reducer 函数需要更新其存储的任何其他信息。
例如,您的 rosterRequest
看起来完全符合我的预期,但 rosterSuccess
应该看起来像:
function rosterSuccess(paylod)
return
type: rosterConstants.GETALL_SUCCESS,
rosterMembers: payload.items,
totalCount: payload.total_count
;
然后,当您 connect
组件时,您可以 mapDispatchToProps
,而不是期望组件直接具有 dispatch
属性。
例如RosterListCard.js
的底部:
RosterListCard.propTypes =
rosterMembers: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
getAllRoster: PropTypes.func.isRequired
;
function mapStateToProps (state)
return
error: null,
isFetching: false,
didInvalidate: false,
totalCount: 0,
rosterMembers: []
;
function mapDispatchToProps(dispatch)
return
getAllRoster: rosterActions.getAll
export default connect(mapStateToProps, mapDispatchToProps)(RosterListCard);
您可以在需要时从this.props
调用该函数:
componentDidMount()
const error, rosterMembers, isFetching = this.props;
this.props.getAllRoster();
【讨论】:
以上是关于redux-thunk:错误:动作必须是普通对象。使用自定义中间件进行异步操作的主要内容,如果未能解决你的问题,请参考以下文章
尝试使用 redux-thunk,但它显示错误,即使对我来说一切都很好
动作必须是普通对象。将自定义中间件用于测试库而不是应用程序上的异步操作