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

使用 Redux Toolkit 开始进行状态管理 DEV 的全球展示与分享挑战赛,由 Mux 呈现:展示你的项目!

使用 Redux Toolkit 开始进行状态管理

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

Redux Toolkit是一个提供辅助函数的库,旨在简化 Redux 的使用方式。它的创建是为了回应 Redux 需要编写大量样板代码且配置复杂等批评。

本系列的前一篇文章探讨了Redux 的状态管理机制。本文将探讨 Redux Toolkit 如何改变这种机制createSlice,并介绍它提供的一些额外功能,例如 `setState`createSelector和 `setState` redux-thunk

如果您想跟着做,我已经为本指南中创建的示例应用程序创建了一个存储库,网址为react-state-comparison

本文假设读者已掌握 React 组件渲染方法,并对 Hooks 的工作原理有基本的了解。此外,由于本文会将 React 与 Redux 进行比较,因此也假设读者对 Redux 有一定的了解。

入门

要开始使用 Redux Toolkit,您需要使用您选择的包管理器安装以下库:

npm install redux react-redux @reduxjs/toolkit
yarn add redux react-redux @reduxjs/toolkit
Enter fullscreen mode Exit fullscreen mode

术语简要概述

在使用 React 和 Redux 管理状态时,有几个术语非常重要:

  • 仓库是我们存储应用程序所有状态的中心位置
  • action负责通知 reducer 修改 store。我们从 UI 分发这些 action
  • 我们还有一些动作创建器,它们是可以为我们创建动作的函数。
  • reducer负责执行 action 指示它执行的操作(即对 store 进行必要的修改)。

使用 createSlice 创建 reducer 和 actions

Redux Toolkit 旨在实现的第一个改进是减少创建 actions 和 reducers 所需的代码量。

使用纯 Redux,以下是我们用来修改待办事项列表名称的代码:

// Action
export const UPDATE_LIST_NAME = 'UPDATE_LIST_NAME';

// Action creator
export const updateListName = (name) => ({
    type: UPDATE_LIST_NAME,
    payload: { name }
});

// Reducer
const reducer = (state = 'My to-do list', action) => {
    switch (action.type) {
        case UPDATE_LIST_NAME: {
            const { name } = action.payload;
            return name;
        }

        default: {
            return state;
        }
    }
};

export default reducer;
Enter fullscreen mode Exit fullscreen mode

使用后createSlice,它看起来像这样:

// src/redux-toolkit/state/reducers/list-name
import { createSlice } from '@reduxjs/toolkit';

const listNameSlice = createSlice({
    name: 'listName',
    initialState: 'My to-do list',
    reducers: {
        updateListName: (state, action) => {
            const { name } = action.payload;
            return name;
        }
    }
});

export const {
    actions: { updateListName },
} = listNameSlice;

export default listNameSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

有了它createSlice,我们只需要定义 reducer,系统就会自动为我们创建 action!这有助于减少开发人员需要编写的样板代码量。

你不再需要担心状态突变的问题了。

使用纯 Redux 时,必须小心不要直接修改状态,否则会导致意想不到的行为。例如,要向 store 添加一个新任务,你需要这样做:

{ ...tasks, [id]: { id, name, checked: false } };
Enter fullscreen mode Exit fullscreen mode

有了它createSlice,我们现在可以直接改变状态:

tasks[id] = { id, name, checked: false };
Enter fullscreen mode Exit fullscreen mode

后台使用的是immer库,所以我们实际上并没有改变状态。

我非常喜欢这个功能,因为它让 reducer 代码更容易理解,也让开发者不必学习如何使用像 immer 这样的库。

创建并初始化我们的商店

创建 reducer 之后,就可以使用它来设置 store 了。这里我们将使用configureStore工具包中的函数来完成这项操作:

import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import reducer from '../reducers';

const store = configureStore({
    reducer
});

export const TasksProvider = ({ children }) => (
    <Provider store={store}>{children}</Provider>
);
Enter fullscreen mode Exit fullscreen mode

configureStore它与 Redux 的区别createStore在于,它提供了一些开箱即用的额外默认设置。这有助于你在设置新应用时更快地入门。redux-thunk稍后我会在本文中介绍它提供的其中一项功能()。

configureStore 可以组合 reducer

Redux 提供了一个combineReducers()函数,可以将你的 reducer 拆分成多个文件:

import { combineReducers } from 'redux';

import listNameReducer from './list-name';
import tasksReducer from './tasks';

const reducer = combineReducers(listNameReducer, tasksReducer);

export default reducer;
Enter fullscreen mode Exit fullscreen mode

使用 `.reducer` configureStore,如果您传入多个 reducer,它将combineReducer为您执行此步骤:

const store = configureStore({
    reducer: {
        listName: listNameReducer,
        tasks: tasksReducer 
    }
});
Enter fullscreen mode Exit fullscreen mode

最后完成

从这一点开始,我们的应用就和使用 Hooks 设置一个普通的 Redux 应用完全相同了。总而言之,我们首先需要将应用包装在TasksProvider我们创建的 `<script>` 标签中:

// src/redux-toolkit/components
const ReduxToolkitApp = () => (
    <>
        <h2>Redux Toolkit</h2>
        <TasksProvider>
            <Name />
            <Tasks />
            <CreateTask />
        </TasksProvider>
    </>
);
Enter fullscreen mode Exit fullscreen mode

使用以下方式创建和使用选择器useSelector

// src/redux-toolkit/state/selectors
export const tasksSelector = (state) => state.tasks;

// src/redux-toolkit/components/tasks
const tasks = useSelector(tasksSelector);
Enter fullscreen mode Exit fullscreen mode

并分发操作来修改状态useDispatch

// src/redux-toolkit/components/name
const dispatch = useDispatch();
dispatch(updateListName({ name }));
Enter fullscreen mode Exit fullscreen mode

现在让我们来看看 React Toolkit 提供的其他几个功能。

创建选择器

Redux Toolkit 允许我们使用 reselect 创建选择器createSelector。这是一个预先存在的功能,只需添加reselect库即可在任何 Redux 应用中使用它。Redux Toolkit 已将其默认包含在内。

什么时候需要使用 createSelector?

使用选择器,组件只会在选择器返回的内容发生变化时才重新渲染。

例如,我们的组件只有在发生变化Tasks时才会重新渲染。state.tasks

// src/redux-toolkit/state/selectors
export const tasksSelector = (state) => state.tasks;

// src/components/redux-toolkit/components/tasks
const Tasks = () => {
    const tasks = useSelector(tasksSelector);

    return <TasksView Task={Task} tasks={tasks} />;
};
Enter fullscreen mode Exit fullscreen mode

然而,每次状态发生改变时tasksSelector都会重新运行并计算需要返回的值。如果我们的任务列表非常庞大,并且选择器需要进行某种计算(例如,根据任务是否已完成进行筛选),这可能会导致性能问题。

createSelector允许你创建记忆化选择器。这意味着它会缓存计算结果,只有在条件发生变化时才会重新计算。在我们的例子中,我们可以创建一个仅在条件state.tasks发生变化时才重新计算的选择器:

import { createSelector } from '@reduxjs/toolkit';

const completedTasksSelector = createSelector(
  state => state.tasks,
  tasks => tasks.filter(task => task.checked)
)
Enter fullscreen mode Exit fullscreen mode

要了解有关 createSelector 的更多信息,我建议您查看reselect库的 readme 文件。

Redux 中间件和 redux-thunk

本文最后要介绍的是Redux 中间件。这些是第三方库,可以添加到你的 Redux 配置中以增加额外的功能。使用 Redux Toolkit 的configureStoreAPI 时,系统会自动安装一些中间件。其中一个就是redux-thunk……

我们什么时候需要 redux-thunk?

一个常见的用例redux-thunk是进行 API 调用。假设我们将新创建的任务存储在后端。我们的流程大致如下:

  • 用户输入内容,然后按下“创建任务”按钮
  • 我们称之为创建任务端点
  • 我们等待端点返回任务已成功创建的消息。
  • 我们在列表底部显示新创建的任务。

我们可以通过以下几种方式来应对这种情况。

方案一:让用户界面调用端点

我们可以让用户界面调用任务创建端点,只有在它成功返回后,我们才调用我们的createTask操作:

// component code
const dispatch = useDispatch();

createTaskAPI(data).then((newTask) => {
    dispatch(createTask(newTask));
});
Enter fullscreen mode Exit fullscreen mode

不过,一般来说,你应该让组件代码专注于渲染内容,并将与 API 交互的代码移到其他地方。

所以如果用户界面能做到这一点就太好了:

dispatch(createTask(data));
Enter fullscreen mode Exit fullscreen mode

然后在动作创建器内部,我们等待 API 返回结果后再返回动作:

const createTask = async (data) => {
    const newTask = await createTaskAPI(data);
    return { type: 'createTask', payload: newTask };
}
Enter fullscreen mode Exit fullscreen mode

但是该dispatch函数需要一个操作——我们不能传入一个异步函数!

选项 2:传入一个调度参数

解决方法是将该dispatch函数作为参数传递,然后等待端点返回后再分发操作:

// component code
const dispatch = useDispatch();
createTask(dispatch, data);

// action creator
const createTask = async (dispatch, data) => {
    const newTask = await createTaskAPI(data);
    dispatch({ type: 'createTask', payload: newTask });
}
Enter fullscreen mode Exit fullscreen mode

这种方法的缺点在于,你的组件代码现在必须知道,对于某些操作,它应该这样做:

action(dispatch, data);
Enter fullscreen mode Exit fullscreen mode

但对于其他操作,它却是这样处理的:

dispatch(action(data))
Enter fullscreen mode Exit fullscreen mode

方案三:使用 redux-thunk

redux-thunk它简化了这一过程,使我们能够访问dispatch操作创建器内部的函数:

// component code
const dispatch = useDispatch();
dispatch(createTask(data));

// action creator
const createTask = (data) => {
    return async (dispatch) => {
        const newTask = await createTaskAPI(data);
        dispatch({ type: 'createTask', payload: newTask });
    }   
}
Enter fullscreen mode Exit fullscreen mode

现在我们的组件代码不再需要知道是否需要传入dispatch参数,而可以始终将操作包装在dispatch函数中!

由于此中间件非常有用,因此在使用设置商店时,默认情况下会包含它configureStore()

结论

Redux Toolkit 是一个非常棒的库,可以简化你编写的 Redux 代码。它默认还提供了许多代码库都需要的实用工具,例如 `get_require()`createSelector和 `get_require( redux-thunk)`。如果你是一名正在开发新代码库的开发者,我建议你直接使用这个工具包。

不幸的是,对于开发较旧、规模较大的代码库的开发人员来说,他们最终可能不得不理解多种 Redux 编写方式:

  • 旧版重制版connect()
  • 带有钩子的新版 ReduxuseSelector()
  • 全新 Redux 和 Redux Toolkit

我预感到这可能会变得令人困惑且难以管理,尤其对于刚接触代码库的开发者而言。另外,这个工具包放在一个独立的库中,而不是作为主 react-redux 库的一部分,这一点也挺有意思的。我理解这样做可能有其合理的原因,但我认为这可能会让 Redux 新手误以为它是可选的,而实际上它应该成为强制性的,并成为新的标准。

不过,我认为这是简化 Redux 的一大进步,我很想看看它未来的发展方向。

感谢阅读!

文章来源:https://dev.to/emma/getting-started-with-state-management-using-redux-toolkit-m80