Redux 数据流程与应用状态的维护技巧

什么是 Redux

Redux 是一种 JavaScript 应用程序的状态容器,它可以管理应用程序的状态并控制它们是如何被处理的。Redux 非常适合于大型应用程序,因为它可以帮助开发者更好地组织和维护应用程序的代码。

在 Redux 中,数据流向是单向的:从视图(即用户操作)到 action,再到 reducer,最后到 store。下面就让我们逐一了解每一个流程。

数据流程

视图

Redux 数据流程的开始就是视图。视图层通过用户操作(例如按钮点击或者输入框输入)来发起一个 action。

action 是一个带有识别信息和有效载荷的 JavaScript 对象。它只描述了发生了什么,但是没有具体的实现。它们显示了一个事件所对应的类型,并且传递给 reducer 进行处理。

例如,当用户单击增加按钮:

const increaseButton = () => {
    return {
        type: 'INCREASE_COUNT'
    }
}

Action

Action 的主要目的是发出指令,改变状态(state)的唯一途径就是通过 dispatch 发布一个 action。

Action 只是一个描述文件,它描述了一个事件对于应用的状态改变。

对应的 reducer 就会根据这个 action 的 type,进行相应的状态的更新。

例如,在单击增加按钮时,将会执行 INCREASE_COUNT 的 action,这个 action 将告诉 reducer 状态要加 1:

Reducer

Reducer 是一个纯函数,它接受现有的状态和一个 action,然后返回新的状态。Reducer 的职责是处理 action 并更新应用程序的状态。

Reducer 将不同的 Action 分类进行处理,并通过返回新的状态来反馈给 Redux Store。

例如,当收到 INCREASE_COUNT action 时,reducer 将会让状态加 1:

const countReducer = (state = 0, action) => {
    switch (action.type) {
        case 'INCREASE_COUNT':
            return state + 1
        default:
            return state
    }
}

Store

Store 将处理过的 state 保存,并在视图中被访问到。Action 是通过 Store.dispatch() 方法发起的,Store 将通过 Reducer 来更新应用程序的状态。Store 是纯粹的 JavaScript 对象,保存了应用程序的 state。

例如,将 countReducer 放入 Store 中:

import { createStore } from 'redux'
import countReducer from './reducers'

const store = createStore(countReducer)

现在,我们已经在 Redux Store 中设置了状态,重要的是如何在项目中使用它。

应用状态的维护技巧

Redux 提供了一种可以管理应用的全局状态的约定方式,这使得你可以清楚的知道状态发生了什么,并且在整个应用程序中保持一致性。

避免过多的嵌套

当你使用一个庞大的 Redux 状态时,你可能会开始看到一些嵌套胡同。你可以使用分离的 reducer,但有时你需要管理并且更新多个数据源,这就是一个非常麻烦的问题。

例如,一个有人员组织结构的应用程序,我们需要保存每个雇员的详细信息和部门的详细信息:

const initialState = {
  employees: [
    {
      name: 'Alex',
      department: 'sales',
      title: 'Senior Sales Executive'
    },
    {
      name: 'Bob',
      department: 'finance',
      title: 'Financial Analyst'
    }
  ],
  departments: [
    {
      name: 'Sales'
    },
    {
      name: 'Finance'
    }
  ]
}

const organizationReducer = (state = initialState, action) => {
    // ...
}

这将使状态看起来十分复杂,因此我们可以将每个子状态分成单独的 reducer。然后我们可以将它们组合成一个大型的 reducer,以帮助我们完成整个数据源的维护。

例如,我们可以将 reducer 拆分为3个子状态管理器:employees、departments 和 organization:

const employeesReducer = (state = [], action) => {
  switch (action.type) {
    case 'ADD_EMPLOYEE':
      return [...state, { id: action.id, ...action.details }]
    case 'REMOVE_EMPLOYEE':
      return state.filter(e => e.id !== action.id)
    case 'UPDATE_EMPLOYEE':
      return state.map(e => e.id === action.id ? { ...e, ...action.updates } : e)
    default:
      return state
  }
}

const departmentsReducer = (state = [], action) => {
  // ...
}

const organizationReducer = combineReducers({
  employees: employeesReducer,
  departments: departmentsReducer
})

现在,我们将 employeeReducer 独立作为一个子状态管理器,这使我们可以更好地维护员工信息的修改。同样,我们也可以更细致地管理部门信息的修改。

将状态进行拆分

如果你的状态看起来非常长,你可能想要考虑将它拆分成更小的子状态。

例如,我们的应用程序可能已经包含了几个状态:用户信息(User)、过滤器(Filter)、分页(Pagination)和 雇员信息(Employee):

const initialState = {
  user: {},
  pagination: {},
  filter: {},
  employee: {},
  loading: {
    users: true,
    employee: true
  }
}

我们可以根据子状态的类型,将主状态分解成更多的子状态:

const userReducer = (state = {}, action) => { /*...*/ }
const paginationReducer = (state = {}, action) => { /*...*/ }
const filterReducer = (state = {}, action) => { /*...*/ }
const employeeReducer = (state = {}, action) => { /*...*/ }

const loadingReducer = (state = {}, action) => {
  switch (action.type) {
    case 'REQUEST_USERS':
      return { ...state, users: true }
    case 'RECEIVE_USERS':
      return { ...state, users: false }
    case 'REQUEST_EMPLOYEES':
      return { ...state, employee: true }
    case 'RECEIVE_EMPLOYEES':
      return { ...state, employee: false }
    default:
      return state
  }
}

const appReducer = combineReducers({
  user: userReducer,
  pagination: paginationReducer,
  filter: filterReducer,
  employee: employeeReducer,
  loading: loadingReducer
})

这个方法可以减小代码的复杂度,也可以更好地管理和维护你的应用程序。

使用 combineReducers

combineReducers 负责将多个 reducer 合并成一个 reducer,它的核心工作是通过依次执行多个 reducer 并合并他们的结果。combineReducers 非常适合管理多种状态,例如上面提到的 User、Filter、Pagination 和 Employee。

例如,我们将 User、Filter 和 Employee 合并到一个 Reducer 中:

const appReducer = combineReducers({
  user: userReducer,
  filter: filterReducer,
  employee: employeeReducer
})

我们已经按类型将大型的状态拆分成了多个子状态,现在我们可以使用 combineReducers() 函数即可将它们合并成一个单一的状态。

示例代码

以下是对应示例代码,基于这个示例,你可以更好地理解 Redux 数据流程:

import { createStore, combineReducers } from 'redux'

// Actions
const increaseCounter = () => ({
  type: 'INCREASE_COUNTER'
})

// Reducers
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREASE_COUNTER':
      return state + 1
    default:
      return state
  }
}

const reducers = combineReducers({
  counter: counterReducer
})

// Store
const store = createStore(reducers)

// Subscribe to store updates
store.subscribe(() => {
  console.log(store.getState())
})

// Dispatch actions
store.dispatch(increaseCounter())
store.dispatch(increaseCounter())

输出

总结

Redux 是一个非常强大且灵活的状态容器。在实际项目使用中,当应用程序状态变得非常复杂时,应该进行状态拆分,并将其分为多个子状态管理器,这使得每个部分都能更好地聚焦于其任务。

重要的是,理解整个数据流程,这将使你在应用中更清晰地管理状态、更好地组织代码和更轻松地更新状态。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a1d2d4add4f0e0ff9da372


纠错反馈