[翻译&摘抄] 在大型应用中使用 Redux 的五个技巧

Redux,用于管理应用程序状态,它的特点包括:
1、单向数据流
2、不可变数据
3、状态变化由 action 触发,导致 reducer 函数返回一个新状态

以下的几点讨论应该可以帮助到任何在大型、数据密集型应用中使用 Redux 的开发者:

第一点: 在存储和访问状态时使用索引和选择器
第二点: 把数据对象,对数据对象的修改以及其它 UI 状态区分开
第三点: 在单页应用的不同页面间共享数据,以及何时不该这么做
第四点: 在状态中的不同节点复用通用的 reducer 函数
第五点: 连接 React 组件与 Redux 状态的最佳实践

使用索引(index)保存数据,使用选择器(selector)读取数据

选择正确的数据结构可以对程序的结构和性能产生很大影响!

例如来自 /users 服务的数据。假设直接将这个普通数组原封不动地存储在状态中,当我们需要获取一个特定用户对象时,就需要遍历状态中的所有用户。如果用户很多,这可能会是一个代价高昂的操作。如果我们想跟踪用户的一小部分,例如选中和未选中的用户呢?我们要么需要把数据保存在两个数组中,要么就要记录这些选中和未选中用户在主数组中的索引。

在这种情况下,重构代码,改用索引的方式存储数据将会是更明智的选择,在 reducer 中,将数据结构改为:

{
 "usersById": {
    123: {
      id: 123,
      name: "Jane Doe",
      email: "jdoe@example.com",
      phone: "555-555-5555",
      ...
    },
    ...
  }
}

如果数据结构被重构成了这样,那么如何通过这种数据结构来展示一个简单的用户列表呢。为此,我们需要使用一个选择器,它是一个接收状态并返回所需数据的函数。一个简单的例子是一个返回状态中所有用户的函数:

const getUsers = ({ usersById }) => {
  return Object.keys(usersById).map((id) => usersById[id]); // 返回了一个用户数据数组
}

将标准状态与视图状态、编辑状态分隔开

首先,标准状态指的是从服务返回的数据。除了标准状态,store 中还包含很多其他类型的数据,例如用户界面组件的状态等等。当首次从 API 读取到标准状态时,我们可能会想将其与页面的其他状态保存在同一个 reducer 文件中。这种方式可能很省事,但当你需要从不同数据源获取多种数据时,它就会变得难以扩展。

相反,我们会把标准状态保存在它单独的 reducer 文件中。这会促使你编写组织更加良好、更加模块化的代码。
垂直扩展 reducer(增加代码行数)比水平扩展 reducer(在 combineReducers 调用中引入更多的 reducer)的可维护性要差。将 reducers 拆分到各自的文件中有利于复用这些 reducer。

合理地在视图之间共享状态

在考虑共享状态时,请思考以下几个问题:

1、有多少视图或者其他 reducer 依赖此部分数据?
2、每个页面是否都需要这些数据的副本?
3、这些数据的改动有多频繁?

在状态之间复用 reducer 函数

在编写了一些 reducer 函数之后,我们可能想要在状态中的不同节点间复用 reducer 逻辑。

在 Redux 中复用 reducer 逻辑可能会有点棘手。默认情况下,当触发一个 action 时所有的 reducer 都会被执行。如果我们在多个 reducer 函数中共享一个 reducer 函数,那么当触发一个 action 时所有这些 reducer 都会被调用。然而这并不是我们想要的结果。当我们读取用户得到总数是 500 时,我们不想域名的 count 也变成 500。

我们推荐两种不同的方式来解决此问题,利用特殊作用域(scope)或是类型前缀(prefix)。第一种方式涉及到在 action 传递的数据中增加一个类型信息。

React 集成与包装

使用 react-redux 库,可以将状态中的数据映射到组件的 props 中。

const ConnectedComponent = connect(
  (state) => { // mapStateToProps
    return {
      users: selectors.getCurrentUsers(state),
      editingUser: selectors.getEditingUser(state),
      ... // 其它来自状态的 props
    };
  }),
  (dispatch) => { // mapDispatchToProps
    const actions = {
      ...actionCreators, // other normal actions
      setPagination: actionCreatorFactories.setPaginationFor('USERS_'),
    };
    return bindActionCreators(actions, dispatch);
  }
)(UsersComponent);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值