发布于 2026-01-06 1 阅读
0

Redux Toolkit 已启动并运行

Redux Toolkit 已启动并运行

几周前我写了一篇关于如何使用 Redux 类和 Hooks 的文章。在这篇文章中,我将使用 Redux Toolkit 重写我之前创建的示例应用程序。

RTK(Redux Toolkit)是一种编写 Redux 的新方法。它消除了 Redux 中一些令人烦恼的部分,例如分散的文件结构、存储配置和繁琐的样板代码。

RTK 为我们提供了一些很棒的功能和实用工具,可以帮我们处理很多重复性的代码。

由于这只是一个简单的入门示例,我强烈建议您查看官方文档,我认为官方文档写得非常好。

这是最终代码。如果想查看“原始”版本,请转到主分支。

我们开始吧

先决条件

  • 我用过 Redux,了解它的基本原理。

目标

  • 将“原生 Redux”应用转换为“RTK”应用。



首先做一些简要说明,然后我们直接来看代码。

要安装 RTK,请运行

npm install --save @reduxjs/toolkit

配置存储

在典型的 Redux 应用中,我们使用 `setstore` 来设置 store createStore。这当然没问题,但大多数情况下,你只是复制粘贴代码,这很容易导致 bug。`setstore` configureStore 可以解决这个问题。它还能合并切片 reducer 和我们可能使用的任何中间件。
它默认使用 Redux Thunk,并启用 Redux DevTools。

由于我们默认应用了一些中间件,如果想要应用更多中间件,则必须显式定义所有中间件。默认的中间件将不再生效。在这种情况下,我们可以像getDefaultMiddleware这样使用。


const store = configureStore({
  reducer: rootReducer,
  middleware: [...getDefaultMiddleware(), our middleware]
})


创建操作

在常规的 Redux 中,我们会创建一个 action creator,它返回一个我们在其他地方声明的 action type 和一个 payload。
createAction这是一个辅助函数,可以简化整个过程。

常规版

import { ADD_SONG } from "./types" // somewhere else

export const addSong = song => {
    return {
        type: ADD_SONG,
        payload: song,
    }
}

使用 createAction

export const addSong = createAction("ADD_SONG")

创建/减少

在常规的 Redux 中,通常的做法是在 switch 语句中循环遍历 action 类型,并返回用于更新状态的指定函数。而 `is_state`createReducer进一步简化了这一过程。它接受两个参数:第一个参数是初始状态,第二个参数是包含所有 action 的对象。它默认使用Immer 库,允许我们使用类似可变语法的语法编写不可变代码。(看起来像是在修改状态,但实际上并没有。)

常规版

export default function(state = initialState, action) {
    switch (action.type) {
        case ADD_SONG:
            return {
                songs: [action.payload, ...state.songs],
            }
        default:
            return state
    }
}

使用 createReducer

export default createReducer(initialState, {
    [addSong]: (state, action) => {
        state.push(action.payload)
    },
}

创建切片

如果我们想更进一步,可以使用createSlice某种结合了 ` createActionsand` 和 ` .` 的工具
createReducer。它本质上是一个函数,接受初始状态、reducer 对象(包含函数)和“切片”名称。系统会自动为我们创建 action。

所以让我们重写一下,看看如何应用上述内容。
首先,我们将使用createAction和来进行操作,createReducer然后再使用createSlice

为了保持文件整洁,我们将把所有操作都移到这个songReducers文件中。您可以尝试不同的文件夹结构,找到最适合您的方案。

import { createAction } from "@reduxjs/toolkit"

// Actions
export const addSong = createAction("ADD_SONG")

export const removeSong = createAction("DELETE_SONG")

export const editSong = createAction("EDIT_SONG")

export const updateSong = createAction("UPDATE_SONG", function prepare(
    title,
    index
) {
    return {
        payload: {
            title,
            index,
        },
    }
})

export const cancelEdit = createAction("CANCEL_EDIT")

这里有两点需要注意。

  1. 我们不需要导入任何操作类型。
  2. 由于有效载荷是隐式返回的,因此我们不需要手动输入有效载荷。

你会注意到它updateSong还有一些额外的功能。

很多时候,您可能需要传递多个参数或为操作添加额外的逻辑。您可以使用该prepare函数轻松实现这一点。在本例中,我们需要两个参数:标题和索引。

现在让我们重写 reducer。

我们导入我们的模块createReducer。我们向它传递初始状态和一个包含所有操作的对象,就像这样。不需要 switch 语句。只需要操作的名称即可。

const initialState = [
    { title: "I love redux", editing: false },
    { title: "The redux song", editing: false },
    { title: "Run to the redux hill", editing: false },
]

// Reducer
export default createReducer(initialState, {
    [addSong]: (state, action) => {
        state.push(action.payload)
    },
    [removeSong]: (state, action) => {
        state.splice(action.payload, 1)
    },
    [editSong]: (state, action) =>
        state.map((song, i) =>
            i === action.payload
                ? { ...song, editing: true }
                : { ...song, editing: false }
        ),
    [updateSong]: (state, action) =>
        state.map((song, i) =>
            i === action.payload.index
                ? { ...song, title: action.payload.title, editing: false }
                : song
        ),
    [cancelEdit]: (state, action) =>
        state.map((song, i) =>
            i === action.payload ? { ...song, editing: false } : song
        ),
})

现在我们可以这样做,createSlice这将使物品更加整洁紧凑。

createSlice它将接收一个初始状态、一个 reducer 对象和一个切片名称。它会自动生成与 reducer 同名的 action。

你可以再次尝试不同的文件夹结构和命名规则。
你可以参考Ducks 的方法来打包你的 reducer 和 action。
这里我创建了一个名为 features 的文件夹和一个名为 . 的文件songSlice。别忘了index在 reducer 文件夹中的文件中导入它。

它看起来大概是这样的。

请注意,看起来好像我在直接改变状态,但实际上并没有。

import { createSlice } from "@reduxjs/toolkit"

const songSlice = createSlice({
    name: "songs",
    initialState: [
        { title: "I love redux", editing: false },
        { title: "The redux song", editing: false },
        { title: "Run to the redux hill", editing: false },
    ],
    reducers: {
        addSong: (state, action) => {
            state.push(action.payload)
        },
        removeSong: (state, action) => {
            state.splice(action.payload, 1)
        },
        editSong: (state, action) => {
            const song = state[action.payload]
            song.editing = true
        },
        updateSong: {
            reducer(state, action) {
                const { title, index } = action.payload
                const song = state[index]
                song.title = title
                song.editing = false
            },
            prepare(title, index) {
                return { payload: { title, index } }
            },
        },
        cancelEdit: (state, action) => {
            const song = state[action.payload]
            song.editing = false
        },
    },
})

export const {
    addSong,
    removeSong,
    editSong,
    updateSong,
    cancelEdit,
} = songSlice.actions

export default songSlice.reducer

就是这样。希望你喜欢。

我认为RTK是Redux发展历程中的一大步,我很想看看它未来会如何发展。

特别感谢Mark Erikson在 Twitter 上提供的更正和反馈。

文章来源:https://dev.to/john2220/up-and-running-with-redux-toolkit-20j7