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")
这里有两点需要注意。
- 我们不需要导入任何操作类型。
- 由于有效载荷是隐式返回的,因此我们不需要手动输入有效载荷。
你会注意到它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