理解 Redux 中 Reducer 的用法

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

Reducer 是一个 **函数**,它决定了 **应用程序状态** 的 **变化**。它使用接收到的 **action** 来确定这种变化。我们有一些工具,比如 Redux,可以帮助我们在一个单一的存储中管理应用程序的状态变化,以便它们的行为保持一致。

为什么我们在谈论 reducer 时会提到 Redux?Redux 严重依赖 reducer 函数,这些函数接收先前的状态和一个 action 以执行下一个状态。

这篇文章我们将重点关注 reducer。我们的目标是让我们对 reducer 函数的工作方式感到舒适,以便我们能够看到它是如何用于更新应用程序状态的——并最终理解它们在 Redux 等状态管理器中所扮演的角色。

“状态”的含义

状态变化基于用户的交互,甚至像网络请求这样的事情。如果应用程序的状态由 Redux 管理,则更改会发生在 reducer 函数内部——这是唯一发生状态更改的地方。reducer 函数利用应用程序的 **初始状态** 和称为 **action** 的东西来确定 **新状态** 将是什么样子。

如果我们在上数学课,我们可以说

initial state + action = new state

就实际的 reducer 函数而言,它看起来像这样

const contactReducer = (state = initialState, action) => {
  // Do something
}

我们从哪里得到初始状态和 action?这些是我们定义的东西。

state 参数

传递给 reducer 函数的 state 参数必须是应用程序的当前状态。在这种情况下,我们将其称为 initialState,因为它将是第一个(也是当前的)状态,并且没有任何东西会先于它。

contactReducer(initialState, action)

假设我们应用程序的初始状态是一个空的联系人列表,我们的 action 是向列表中添加一个新的联系人。

const initialState = {
  contacts: []
}

这创建了我们的 initialState,它等于 reducer 函数所需的 state 参数。

action 参数

action 是一个对象,包含两个键及其值。reducer 中发生的 state 更新始终取决于 action.type 的值。在本场景中,我们演示了当用户尝试创建新联系人时会发生什么。因此,让我们将 action.type 定义为 NEW_CONTACT

const action = {
  type: 'NEW_CONTACT',
  name: 'John Doe',
  location: 'Lagos Nigeria',
  email: '[email protected]'
}

通常还有一个 payload 值,其中包含用户发送的内容,并将用于更新应用程序的状态。需要注意的是,action.type 是必需的,但 action.payload 是可选的。使用 payload 为 action 对象的外观带来了一定程度的结构。

更新状态

状态应该为 不可变,这意味着不应该直接更改它。要创建更新的状态,我们可以使用 Object.assign 或选择 扩展运算符

Object.assign

const contactReducer = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return Object.assign({}, state, {
      contacts: [
        ...state.contacts,
        action.payload
      ]
    })
    default:
      return state
  }
}

在上面的示例中,我们使用了 Object.assign() 来确保我们不会直接更改 state 值。相反,它允许我们返回一个新对象,该对象填充了传递给它的 state 和用户发送的 payload。

要使用 Object.assign(),重要的是第一个参数必须是一个空对象。将 state 作为第一个参数传递会导致它被修改,这正是我们为了保持一致性而试图避免的。

扩展运算符

object.assign() 的替代方法是使用扩展运算符,如下所示

const contactReducer = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return {
        ...state, contacts:
        [...state.contacts, action.payload]
    }
    default:
      return state
  }
}

这确保了传入的 state 保持不变,因为我们将新项追加到底部。

使用 switch 语句

前面我们注意到发生的更新取决于 action.type 的值。switch 语句根据 action.type 的值有条件地确定我们正在处理的更新类型。

这意味着一个典型的 reducer 会像这样

const addContact = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return {
        ...state, contacts:
        [...state.contacts, action.payload]
    }
    case 'UPDATE_CONTACT':
      return {
        // Handle contact update
      }
    case 'DELETE_CONTACT':
      return {
        // Handle contact delete
      }
    case 'EMPTY_CONTACT_LIST':
      return {
        // Handle contact list
      }
    default:
      return state
  }
}

当 action 对象中指定的 action.type 值与 reducer 中的值不匹配时,我们必须在 default 中返回 state——例如,如果由于某种未知原因,action 看起来像这样

const action = {
  type: 'UPDATE_USER_AGE',
  payload: {
    age: 19
  }
}

由于我们没有这种 action 类型,因此我们希望返回 state 中的内容(应用程序的当前状态)。这意味着我们目前不确定用户想要实现什么。

整合所有内容

这是一个关于我在 React 中如何实现 reducer 函数的简单示例。

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的
reducer 示例

CodePen 上。

您可以看到我没有使用 Redux,但这与 Redux 使用 reducer 存储和更新状态更改的方式非常相似。主要的状态更新发生在 reducer 函数中,它返回的值设置了应用程序的更新状态。

想尝试一下吗?您可以扩展 reducer 函数以允许用户更新联系人的年龄。我希望看到您在评论区分享您的成果!

了解 reducer 在 Redux 中扮演的角色,应该可以帮助您更好地理解幕后发生的事情。如果您有兴趣阅读更多关于在 Redux 中使用 reducer 的内容,值得查看 官方文档