我有一个 React 应用程序,我需要使用 Redux 对在线服务(异步)进行 ajax 调用(以便学习)。
这是我的商店:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'
export default applyMiddleware(thunk)(createStore)(duedates);
这是操作:
import rest from '../Utils/rest';
export function getDueDatesOptimistic(dueDates){
console.log("FINISH FETCH");
console.log(dueDates);
return {
type: 'getDueDate',
dueDates
}
}
export function waiting() {
console.log("IN WAIT");
return {
type: 'waiting'
}
}
function fetchDueDates() {
console.log("IN FETCH");
return rest({method: 'GET', path: '/api/dueDates'});
}
export function getDueDates(dispatch) {
console.log("IN ACTION");
return fetchDueDates().done(
dueDates => dispatch(getDueDatesOptimistic(dueDates.entity._embedded.dueDates))
)
}
这是减速器:
export default (state = {}, action) => {
switch(action.type) {
case 'getDueDate':
console.log("IN REDUCER")
return state.dueDates = action.dueDates;
default:
return state
}
}
我不明白我做错了什么。从组件完美地调用了该操作。但是后来我收到此错误:
错误:操作必须是普通对象。使用自定义中间件执行异步操作。
我想我使用了错误的反应中间件。我做错了什么?
编辑
现在操作正在调用化简器,但化简器在更改状态后不会重新运行 render 方法
case 'getDueDate':
console.log("IN REDUCER")
return state.dueDates = action.dueDates;
我认为你应该使用compose
函数,所以它就像
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'
export default compose(applyMiddleware(thunk))(createStore)(duedates);
Thunk 允许动作创建者返回函数而不是普通对象,因此您可以像
export function getDueDates() {
return dispatch => {
console.log("IN ACTION");
fetchDueDates().done(
dueDates => dispatch(getDueDatesOptimistic(dueDates.entity._embedded.dueDates))
)
};
}
您返回了一个 Promise 对象,这是问题的一部分。另一部分是redux-thunk没有被正确应用。我敢打赌compose
应该解决问题。
接受的答案要么过时,要么错误,要么过于复杂。以下是有关撰写主题的文档:
http://redux.js.org/docs/api/compose.html
因此,我们可以这样做:
import {createStore, combineReducers, compose, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
const reducer = combineReducers({
user: userReducer,
items: itemsReducer
});
// here is our redux-store
const store = createStore(reducer,
compose(applyMiddleware(thunk))
);
也可以在不使用compose
函数的情况下获得有效的解决方案。基于 GitHub 存储库中的文档 redux-thunk
解决方案适用于redux-thunk: ^2.0.0
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import duedates from './reducers/duedates'
export function configureStore(initialState = {}) {
return createStore(
duedates,
initialState,
applyMiddleware(thunk)
);
}
我看到多个语法不同的答案。我们将提出一个解决方案,而无需手动应用中间件,也不必单独安装redux-thunk
。
唯一的 redux 依赖项: @reduxjs/toolkit: ^1.8.6
src/redux/store.js
import { configureStore } from "@reduxjs/toolkit";
import category from './features/category.feature';
const store = configureStore({
reducer: {
category
}
});
export default store;
src/redux/features/category.feature.js
import { createSlice } from "@reduxjs/toolkit";
import { XHR_STATUS } from "../../misc/enums";
import axios from 'axios';
import { BASE_URL } from "../../misc/constants/xhr";
const initialState = {
data: [],
status: XHR_STATUS.NEUTRAL,
errorMessage: ''
};
let slice = createSlice({
name: 'category',
initialState,
reducers: {
setStatus: (state, actions) => {
state.status = actions.payload;
},
setData: (state, action) => {
state.data = action.payload;
}
}
});
export const { setStatus, setData } = slice.actions;
export const fetchCategories = () => {
return async (dispatch) => {
dispatch(setStatus(XHR_STATUS.LOADING));
const response = await axios.get(`${BASE_URL}categories`);
const { data } = response;
dispatch(setData(data));
dispatch(setStatus(XHR_STATUS.SUCCESS));
}
}
export default slice.reducer;
src/components/Categories/index.js
import { XHR_STATUS } from '../../misc/enums';
import { ListItem, List } from '@mui/material';
import CategoryBox from '../CategoryBox'; // This is my custom component to render a single Category
function Categories (props) {
const { category } = props;
switch(category.status) {
case XHR_STATUS.NEUTRAL:
return (
'Waitng...'
);
case XHR_STATUS.LOADING:
return (
'Loading...'
);
case XHR_STATUS.SUCCESS:
return (
<List style={{ padding: '20px 0 20px 0', display: 'inline-flex', flexDirection: 'row', overflowX: 'scroll', width: '100%' }}>
{category.data.map((c, i) => (
<ListItem key={i}>
<CategoryBox category={c} />
</ListItem>
))}
</List>
);
case XHR_STATUS.ERROR:
return (
'Error...'
);
}
}