reactjs redux 动作必须是普通对象
reactjs redux Actions must be plain objects
我有一个问题“操作必须是普通对象。使用自定义中间件进行异步操作。”
我在这个样板中使用 reactjs (https://github.com/react-boilerplate/react-boilerplate/)
我浪费了 1 天的时间来解决这个问题,但没有结果。我正在尝试将获取请求移动到操作(没有 saga)并且结果相同。
我的组件:
...
import { compose } from 'redux';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import { successFetching } from './actions';
import reducer from './reducers';
import saga from './saga';
class NewsListPage extends React.PureComponent {
componentDidMount() {
const { dispatch } = this.props
dispatch(saga())
}
...
};
NewsListPage.propTypes = {
isFetching: PropTypes.bool.isRequired,
isSuccess: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
}
const selector = (state) => state;
const mapStateToProps = createSelector(
selector,
(isFetching, isSuccess, items) => ({ isFetching, isSuccess,items })
);
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withReducer = injectReducer({ key: 'NewsList', reducer });
const withSaga = injectSaga({ key: 'NewsList', saga });
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
withReducer,
withSaga,
withConnect
)(NewsListPage);
我的操作:
export const NEWS_FETCH_LOADING = 'NEWS_FETCH_LOADING';
export const NEWS_FETCH_FAILURE = 'NEWS_FETCH_FAILURE';
export const NEWS_FETCH_SUCCESS = 'NEWS_FETCH_SUCCESS';
export function preFetching() {
return {
type: NEWS_FETCH_LOADING,
}
}
export function successFetching(json) {
return {
type: NEWS_FETCH_SUCCESS,
payload: json
}
}
export function failureFetching(error) {
return {
type: NEWS_FETCH_FAILURE,
payload: error
}
}
我的减速器:
...
import { NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS } from './actions'
const INITIAL_STATE = {
isFetching: false,
isSuccess: false,
items: []
};
function NewsListReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case NEWS_FETCH_LOADING:
case NEWS_FETCH_FAILURE:
return Object.assign({}, state, {
isFetching: true,
isSuccess: false
})
case NEWS_FETCH_SUCCESS:
return Object.assign({}, state, {
isFetching: false,
isSuccess: true,
items: action.payload,
})
default:
return Object.assign({}, state)
}
}
export const rootReducer = combineReducers({
NewsListReducer
})
export default rootReducer
我的传奇:
import { call, put, select, takeLatest } from 'redux-saga/effects';
import {NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS, preFetching, successFetching, failureFetching} from './actions';
import request from 'utils/request';
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put(successFetching(req));
} catch (err) {
yield put(failureFetching(err));
}
}
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
编辑:
@Alleo Indong,我尝试了你的建议并且几乎成功了。
我将组件 mapDispatchToProps 更改为
export function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(loadNews()),
};
}
并为 actions.js
添加新功能
export function loadNews() {
return {
type: NEWS_FETCH_SUCCESS,
}
}
但现在 ajax 每秒发送一次 就像在 while 循环中一样。我尝试在 componentDidMount
和 constructor
中调用 this.props.getData();
,结果相同。
编辑 2
在我添加的组件中
import * as actionCreators from './actions';
import { bindActionCreators } from 'redux';
在构造函数中我改变了
constructor(props) {
super(props);
const {dispatch} = this.props;
this.boundActionCreators = bindActionCreators(actionCreators, dispatch)
}
但是这里 dispatch
是未定义的,在 componentDidMount
中也是如此。
并将 mapDispatchToProps
更改为
export function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
你好@AND,欢迎来到 Whosebug!正如我在主 post 的评论中提到的,您正在调度一个 GENERATOR 而不是
上的一个对象
dispatch(saga());
这里有一个例子可以帮助你
在您的组件上导入您要像这样使用的操作。
import * actionCreators from './actions';
import { bindActionCreators } from 'redux';
在此处了解有关 bindActionCreators 的更多信息 enter link description here
这将导入您在那里创建的所有导出的 actionCreator。
在我看来,您不再需要 successFetching
和 failureFetching
,因为您可以稍后在您的 saga
中发送此操作
然后在您的 mapDispatchToProps
中注册此 actioncreator
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
那么关于你的故事,我也想在这里指出一些问题。
首先是你的函数
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
实际上是我们所说的 watcher
,它会在某个动作被调度时监视它,在这个函数中你已经在 NEWS_FETCH_SUCCESS
之前等待 getNews
被调用了错了,因为你先做了 FETCHING
然后你才会知道它是失败还是成功所以是的这个函数应该是这样的
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_LOADING, getNews);
}
这个简单意味着您将 take all the latest
NEWS_FETCH_LOADINGactions that was dispatched and will call the
getNews`。
然后在你的getNews
生成器函数上,你可以这样做
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put({type: NEWS_FETCH_SUCCESS, req});
} catch (err) {
yield put({type: NEWS_FETCH_FAILED, err});
}
}
在这里
const req = yield call(request, requestURL);
你说你会等待你调用的请求/服务的结果,这可能是一个承诺。
然后在这里,这就是为什么你不再需要函数 successFetching
和 failureFetching
的原因,因为你可以这样做
yield put({type: NEWS_FETCH_SUCCESS, req});
您现在必须做的最后一个重要步骤是在您的 componentDidMount()
中调用 actionCreator
像这样
componentDidMount() {
const { preFetching } = this.props;
preFetching();
}
我有一个问题“操作必须是普通对象。使用自定义中间件进行异步操作。”
我在这个样板中使用 reactjs (https://github.com/react-boilerplate/react-boilerplate/) 我浪费了 1 天的时间来解决这个问题,但没有结果。我正在尝试将获取请求移动到操作(没有 saga)并且结果相同。
我的组件:
...
import { compose } from 'redux';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import { successFetching } from './actions';
import reducer from './reducers';
import saga from './saga';
class NewsListPage extends React.PureComponent {
componentDidMount() {
const { dispatch } = this.props
dispatch(saga())
}
...
};
NewsListPage.propTypes = {
isFetching: PropTypes.bool.isRequired,
isSuccess: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
}
const selector = (state) => state;
const mapStateToProps = createSelector(
selector,
(isFetching, isSuccess, items) => ({ isFetching, isSuccess,items })
);
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withReducer = injectReducer({ key: 'NewsList', reducer });
const withSaga = injectSaga({ key: 'NewsList', saga });
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
withReducer,
withSaga,
withConnect
)(NewsListPage);
我的操作:
export const NEWS_FETCH_LOADING = 'NEWS_FETCH_LOADING';
export const NEWS_FETCH_FAILURE = 'NEWS_FETCH_FAILURE';
export const NEWS_FETCH_SUCCESS = 'NEWS_FETCH_SUCCESS';
export function preFetching() {
return {
type: NEWS_FETCH_LOADING,
}
}
export function successFetching(json) {
return {
type: NEWS_FETCH_SUCCESS,
payload: json
}
}
export function failureFetching(error) {
return {
type: NEWS_FETCH_FAILURE,
payload: error
}
}
我的减速器:
...
import { NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS } from './actions'
const INITIAL_STATE = {
isFetching: false,
isSuccess: false,
items: []
};
function NewsListReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case NEWS_FETCH_LOADING:
case NEWS_FETCH_FAILURE:
return Object.assign({}, state, {
isFetching: true,
isSuccess: false
})
case NEWS_FETCH_SUCCESS:
return Object.assign({}, state, {
isFetching: false,
isSuccess: true,
items: action.payload,
})
default:
return Object.assign({}, state)
}
}
export const rootReducer = combineReducers({
NewsListReducer
})
export default rootReducer
我的传奇:
import { call, put, select, takeLatest } from 'redux-saga/effects';
import {NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS, preFetching, successFetching, failureFetching} from './actions';
import request from 'utils/request';
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put(successFetching(req));
} catch (err) {
yield put(failureFetching(err));
}
}
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
编辑: @Alleo Indong,我尝试了你的建议并且几乎成功了。
我将组件 mapDispatchToProps 更改为
export function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(loadNews()),
};
}
并为 actions.js
添加新功能export function loadNews() {
return {
type: NEWS_FETCH_SUCCESS,
}
}
但现在 ajax 每秒发送一次 就像在 while 循环中一样。我尝试在 componentDidMount
和 constructor
中调用 this.props.getData();
,结果相同。
编辑 2 在我添加的组件中
import * as actionCreators from './actions';
import { bindActionCreators } from 'redux';
在构造函数中我改变了
constructor(props) {
super(props);
const {dispatch} = this.props;
this.boundActionCreators = bindActionCreators(actionCreators, dispatch)
}
但是这里 dispatch
是未定义的,在 componentDidMount
中也是如此。
并将 mapDispatchToProps
更改为
export function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
你好@AND,欢迎来到 Whosebug!正如我在主 post 的评论中提到的,您正在调度一个 GENERATOR 而不是
上的一个对象dispatch(saga());
这里有一个例子可以帮助你
在您的组件上导入您要像这样使用的操作。
import * actionCreators from './actions';
import { bindActionCreators } from 'redux';
在此处了解有关 bindActionCreators 的更多信息 enter link description here
这将导入您在那里创建的所有导出的 actionCreator。
在我看来,您不再需要 successFetching
和 failureFetching
,因为您可以稍后在您的 saga
然后在您的 mapDispatchToProps
中注册此 actioncreator
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
那么关于你的故事,我也想在这里指出一些问题。 首先是你的函数
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
实际上是我们所说的 watcher
,它会在某个动作被调度时监视它,在这个函数中你已经在 NEWS_FETCH_SUCCESS
之前等待 getNews
被调用了错了,因为你先做了 FETCHING
然后你才会知道它是失败还是成功所以是的这个函数应该是这样的
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_LOADING, getNews);
}
这个简单意味着您将 take all the latest
NEWS_FETCH_LOADINGactions that was dispatched and will call the
getNews`。
然后在你的getNews
生成器函数上,你可以这样做
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put({type: NEWS_FETCH_SUCCESS, req});
} catch (err) {
yield put({type: NEWS_FETCH_FAILED, err});
}
}
在这里
const req = yield call(request, requestURL);
你说你会等待你调用的请求/服务的结果,这可能是一个承诺。
然后在这里,这就是为什么你不再需要函数 successFetching
和 failureFetching
的原因,因为你可以这样做
yield put({type: NEWS_FETCH_SUCCESS, req});
您现在必须做的最后一个重要步骤是在您的 componentDidMount()
像这样
componentDidMount() {
const { preFetching } = this.props;
preFetching();
}