使用 Redux Toolkit 状态管理(一) | 青训营笔记

169 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第1天。

前言

近两日在学习 Redux 状态管理,官方的文档十分优秀,跟随 Redux 循序渐进教程,可以构建一个具有一些实际功能的小型社交媒体供稿应用程序,了解在实践中的实际工作方式,并讨论使用 Redux 的一些重要模式和指南。

本文是对官方文档进行的摘录与总结。

Redux Toolkit

基本概念

Redux 是一个使用叫做“action”的事件来管理和更新应用状态的模式和工具库,以集中式 Store(centralized store)的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。

官方为 Redux 进行了四点总结:可预测、集中管理、可调式和灵活。

应用与全局的状态以对象数的方式存放在唯一的store中,只可以通过action来改变状态树(state tree),action 是描述发生的对象,将其dispatch给 store,便可以对对应的action 进行响应作出更新。

Redux Toolkit 可以编写更精短的代码,更易于阅读,同时仍然遵循同样的 Redux 规范和数据流。

安装

npm install @reduxjs/toolkit 或者 yarn add @reduxjs/toolkit

Redux Toolkit 包含 Redux 核心以及常用的软件包如 :Redux Thunk、Reselect。

也可以快速创建一个 React Redux 应用

npx create-react-app my-app --template redux

核心概念

单向数据流

  • state 描述应用程序在特定时间点的状况
  • 基于 state 来渲染出 View
  • 当发生某些事情时(如单击按钮),state 会根据发生的事情进行更新,生成新的 state
  • 基于新的 state 重新渲染 View

image-20220724214044824.png

然而,当有多个组件需要共享和使用相同state时,可能会变得很复杂

解决这个问题的一种方法是从组件中提取共享 state,并将其放入组件树之外的一个集中位置。这样,我们的组件树就变成了一个大“view”,任何组件都可以访问 state 或触发 action,无论它们在树中的哪个位置!

通过定义和分离 state 管理中涉及的概念并强制执行维护 view 和 state 之间独立性的规则,代码变得更结构化和易于维护。

这就是 Redux 背后的基本思想:应用中使用集中式的全局状态来管理,并明确更新状态的模式,以便让代码具有可预测性。

应用的 state 用普通对象来描述,如:

{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

想要更新 state 数据,需要发起 action,action 也是一个具有type字段的普通对象,用来描述发生了什么:

{ type: 'ADD_TODO', text: 'Go to swimming pool' }

然后利用 reducer 函数来根据 action 描述对 state 更新。reducer 接受 state 和 action 并返回新的 state。

function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter
  } else {
    return state
  }
}

使用

创建 Redux Store

Redux store 使用 Redux Toolkit 中的configureStore创建并传入一个reducer参数。

应用程序可能由许多不同的特性组成,每个特性都可能有自己的 reducer 函数。当调用configureStore 时,我们可以传入一个对象中的所有不同的 reducer。 对象中的键名 key 将定义最终状态树中的键名 key。

import { configureStore } from '@reduxjs/toolkit'export default configureStore({
  reducer: {}
})

当传入一个像 {counter: counterReducer} 这样的对象时,它表示我们希望在 Redux 状态对象中有一个 state.counter 部分,并且我们希望 counterReducer 函数负责决定是否以及如何在 dispatch action 时更新 state.counter 部分。

Redux 切片(Slice)

“切片”是应用中单个功能的 Redux reducer 逻辑和 action 的集合, 通常一起定义在一个文件中。该名称来自于将根 Redux 状态对象拆分为多个状态“切片”。

export default configureStore({
  reducer: {
    users: usersReducer,
    posts: postsReducer,
    comments: commentsReducer
  }
})

其中,usersReducer、postsReducer 和 commentsReducer 都是 Redux state 的一个切片,usersReducer 是负责根性 state.users 的切片,可以将其称为 slice reducer 函数。

创建 Slice reducer 和 Action

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment: state => {
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
})

Reducer 需要符合以下规则:

  • 仅使用 stateaction 参数计算新的状态值
  • 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates)
  • 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码

在 Redux 中,不允许在 reducer 中更改 state 的原始对象。然而,createSlice 内部使用了一个名为 Immer的库。Immer 会跟踪尝试进行的所有更改,然后使用该更改列表返回一个安全的、不可变的更新值,就好像手动编写了所有不可变的更新逻辑一样。