40

我遇到了 Redux Toolkit (RTK),并希望实现它提供的更多功能。我的应用程序分派到通过createSlice({})(参见createSlice api docs)创建的 reducers 切片

到目前为止,这非常有效。我可以轻松地使用内置dispatch(action)useSelector(selector)调度操作,并在我的组件中很好地接收/响应状态变化。

我想使用来自 axios 的异步调用从 API 获取数据并更新存储,因为请求是 A) 开始 B) 完成。

我已经看到redux-thunk并且似乎它完全是为此目的而设计的,但是新的 RTK 在createSlice()接下来的一般谷歌搜索中似乎并不支持它。

以上是使用切片实现 thunk 的当前状态吗?

我在文档中看到您可以添加extraReducers到切片中,但不确定这是否意味着我可以创建更多使用 thunk 的传统减速器并让切片实现它们?

总的来说,这是一种误导,因为 RTK 文档显示您可以使用 thunk,但似乎没有提到它不能通过新的 slices api 访问。

Redux 工具包中间件示例

const store = configureStore({
  reducer: rootReducer,
  middleware: [thunk, logger]
})

我的切片代码显示了异步调用将失败的位置以及其他一些可以工作的示例减速器。

import { getAxiosInstance } from '../../conf/index';

export const slice = createSlice({
    name: 'bundles',
    initialState: {
        bundles: [],
        selectedBundle: null,
        page: {
            page: 0,
            totalElements: 0,
            size: 20,
            totalPages: 0
        },
        myAsyncResponse: null
    },

    reducers: {
        //Update the state with the new bundles and the Spring Page object.
        recievedBundlesFromAPI: (state, bundles) => {
            console.log('Getting bundles...');
            const springPage = bundles.payload.pageable;
            state.bundles = bundles.payload.content;
            state.page = {
                page: springPage.pageNumber,
                size: springPage.pageSize,
                totalElements: bundles.payload.totalElements,
                totalPages: bundles.payload.totalPages
            };
        },

        //The Bundle selected by the user.
        setSelectedBundle: (state, bundle) => {
            console.log(`Selected ${bundle} `);
            state.selectedBundle = bundle;
        },

        //I WANT TO USE / DO AN ASYNC FUNCTION HERE...THIS FAILS.
        myAsyncInSlice: (state) => {
            getAxiosInstance()
                .get('/')
                .then((ok) => {
                    state.myAsyncResponse = ok.data;
                })
                .catch((err) => {
                    state.myAsyncResponse = 'ERROR';
                });
        }
    }
});

export const selectBundles = (state) => state.bundles.bundles;
export const selectedBundle = (state) => state.bundles.selectBundle;
export const selectPage = (state) => state.bundles.page;
export const { recievedBundlesFromAPI, setSelectedBundle, myAsyncInSlice } = slice.actions;
export default slice.reducer;

我的商店设置(商店配置)。

import { configureStore } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';

import bundlesReducer from '../slices/bundles-slice';
import servicesReducer from '../slices/services-slice';
import menuReducer from '../slices/menu-slice';
import mySliceReducer from '../slices/my-slice';

const store = configureStore({
    reducer: {
        bundles: bundlesReducer,
        services: servicesReducer,
        menu: menuReducer,
        redirect: mySliceReducer
    }
});
export default store;
4

3 回答 3

83

我是 Redux 维护者和 Redux Toolkit 的创建者。

FWIW,没有关于使用 Redux Toolkit 进行 Redux 更改的异步调用。

您仍然会使用异步中间件(通常是redux-thunk),获取数据,并使用结果分派操作。

从 Redux Toolkit 1.3 开始,我们确实有一个名为的辅助方法createAsyncThunk,它可以生成动作创建者并为您请求生命周期动作分派,但它仍然是相同的标准过程。

来自文档的示例代码总结了用法;

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'

// First, create the thunk
const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId, thunkAPI) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  }
)

// Then, handle actions in your reducers:
const usersSlice = createSlice({
  name: 'users',
  initialState: { entities: [], loading: 'idle' },
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: (builder) => {
    // Add reducers for additional action types here, and handle loading state as needed
    builder.addCase(fetchUserById.fulfilled, (state, action) => {
      // Add user to the state array
      state.entities.push(action.payload)
    })
  },
})

// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))

有关此主题的更多信息,请参阅Redux 工具包“使用指南:异步逻辑和数据获取”文档页面。

希望这会为您指明正确的方向!

于 2020-02-20T16:56:26.177 回答
13

您可以使用createAsyncThunk创建thunk action,可以使用触发dispatch

团队切片.ts

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
const axios = require("axios");

export const fetchPlayerList = createAsyncThunk(
  "team/playerListLoading",
  (teamId: string) =>
    axios
      .get(`https://api.opendota.com/api/teams/${teamId}/players`)
      .then((response) => response.data)
      .catch((error) => error)
);

const teamInitialState = {
  playerList: {
    status: "idle",
    data: {},
    error: {},
  },
};

const teamSlice = createSlice({
  name: "user",
  initialState: teamInitialState,
  reducers: {},
  extraReducers: {
    [fetchPlayerList.pending.type]: (state, action) => {
      state.playerList = {
        status: "loading",
        data: {},
        error: {},
      };
    },
    [fetchPlayerList.fulfilled.type]: (state, action) => {
      state.playerList = {
        status: "idle",
        data: action.payload,
        error: {},
      };
    },
    [fetchPlayerList.rejected.type]: (state, action) => {
      state.playerList = {
        status: "idle",
        data: {},
        error: action.payload,
      };
    },
  },
});

export default teamSlice;

Team.tsx 组件

import React from "react";
import { useSelector, useDispatch } from "react-redux";

import { fetchPlayerList } from "./teamSlice";

const Team = (props) => {
  const dispatch = useDispatch();
  const playerList = useSelector((state: any) => state.team.playerList);

  return (
    <div>
      <button
        onClick={() => {
          dispatch(fetchPlayerList("1838315"));
        }}
      >
        Fetch Team players
      </button>

      <p>API status {playerList.status}</p>
      <div>
        {playerList.status !== "loading" &&
          playerList.data.length &&
          playerList.data.map((player) => (
            <div style={{ display: "flex" }}>
              <p>Name: {player.name}</p>
              <p>Games Played: {player.games_played}</p>
            </div>
          ))}
      </div>
    </div>
  );
};

export default Team;
于 2020-07-18T04:39:35.557 回答
7

利用redux-toolkit v1.3.0-alpha.8

尝试这个

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

export const myAsyncInSlice = createAsyncThunk('bundles/myAsyncInSlice', () =>
  getAxiosInstance()
    .get('/')
    .then(ok => ok.data)
    .catch(err => err),
);

const usersSlice = createSlice({
  name: 'bundles',
  initialState: {
    bundles: [],
    selectedBundle: null,
    page: {
      page: 0,
      totalElements: 0,
      size: 20,
      totalPages: 0,
    },
    myAsyncResponse: null,
    myAsyncResponseError: null,
  },
  reducers: {
    // add your non-async reducers here
  },
  extraReducers: {
    // you can mutate state directly, since it is using immer behind the scenes
    [myAsyncInSlice.fulfilled]: (state, action) => {
      state.myAsyncResponse = action.payload;
    },
    [myAsyncInSlice.rejected]: (state, action) => {
      state.myAsyncResponseError = action.payload;
    },
  },
});


于 2020-02-20T09:56:17.513 回答