首页 男生 其他 深入浅出React和Redux

5.3.1 两阶段选择过程

深入浅出React和Redux 程墨 4519 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  5.3.1 两阶段选择过程

  既然这个selectVisibleTodos函数的计算必不可少,那如何优化呢?

  实际上,并不是每一次对TodoList组件的重新渲染都必须要执行selectVisibleTodos中的计算过程,如果Redux Store状态树上代表所有待办事项的todos字段没有变化,而且代表当前过滤器的filter字段也没有变化,那么实在没有必要重新遍历整个todos数组来计算一个新的结果,如果上一次的计算结果被缓存起来的话,那就可以重用缓存的数据。

  这就是reselect库的工作原理:只要相关状态没有改变,那就直接使用上一次的缓存结果。

  reselect库被用来创造“选择器”,所谓选择器,就是接受一个state作为参数的函数,这个选择器函数返回的数据就是我们某个mapStateToProps需要的结果。

  在前面的章节,我们已经强调过React组件的渲染函数应该是一个纯函数,Redux中的reducer函数也应该是一个纯函数,mapStateToProps函数也应该是纯函数,纯函数让问题清晰而且简化。不过,现在这个“选择器”函数可不是纯函数,它是一种有“记忆力”的函数,运行选择器函数会有副作用,副作用就是能够根据以往的运行“记忆”返回“记忆”中的结果。

  reselect认为一个选择器的工作可以分为两个部分,把一个计算过程分为两个步骤:

  步骤1,从输入参数state抽取第一层结果,将这第一层结果和之前抽取的第一层结果做比较,如果发现完全相同,就没有必要进行第二部分运算了,选择器直接把之前第二部分的运算结果返回就可以了。注意,这一部分做的“比较”,就是JavaScript的===操作符比较,如果第一层结果是对象的话,只有是同一对象才会被认为是相同。

  步骤2,根据第一层结果计算出选择器需要返回的最终结果。

  显然,每次选择器函数被调用时,步骤一都会被执行,但步骤一的结果被用来判断是否可以使用缓存的结果,所以并不是每次都会调用步骤二的运算。

  选择器就是利用这种缓存结果的方式,避免了没有必要的运算浪费。

  剩下的事情就是确定选择器步骤一和步骤二分别进行什么运算。原则很简单,步骤一运算因为每次选择器都要使用,所以一定要快,运算要非常简单,最好就是一个映射运算,通常就只是从state参数中得到某个字段的引用就足够,把剩下来的重活累活都交给步骤二去做。

  在TodoList这个具体例子中,todos和filter的值直接决定应该显示什么样的待办事项,所以,很显然步骤一是获取todos和filter的值,步骤二就是根据这两个值进行计算。

  使用reselect需要安装对应的npm包:

  npm install –-save reselect

  在src/todos/selector.js文件中,选择器函数的代码如下:

  import {createSelector} from 'reselect';

  import {FilterTypes} from '../constants.js';

  export const selectVisibleTodos = createSelector(

  [getFilter, getTodos],

  (filter, todos) => {

  switch (filter) {

  case FilterTypes.ALL:

  return todos;

  case FilterTypes.COMPLETED:

  return todos.filter(item => item.completed);

  case FilterTypes.UNCOMPPLETED:

  return todos.filter(item => !item.completed);

  default:

  throw new Error('unsupported filter');

  }

  }

  );

  reselect提供了创造选择器的createSelector函数,这是一个高阶函数,也就是接受函数为参数来产生一个新函数的函数。

  第一个参数是一个函数数组,每个元素代表了选择器步骤一需要做的映射计算,这里我们提供了两个函数getFilte和getTodos,对应代码如下:

  const getFilter = (state) => state.filter;

  const getTodos = (state) => state.todos;

  上面说过,步骤一的运算要尽量简单快捷,所以往往一个Lambda表达式就足够。

  createSelector函数的第二个参数代表步骤二的计算过程,参数为第一个参数的输出结果,里面的逻辑和之前TodoList中的逻辑没有什么两样,只是这第二个函数不是每次都会被调用到。

  现在,我们可以在TodoList模块中改用新定义的选择器来获取待办事项数据了,代码如下:

  import {selectVisibleTodos} from '../selector.js';

  const mapStateToProps = (state) => {

  return {

  todos: selectVisibleTodos(state)

  };

  }

  Redux要求每个reducer不能修改state状态,如果要返回一个新的状态,就必须返回一个新的对象。如此一来,Redux Store状态树上某个节点如果没有改变,那么我们就有信心这个节点下数据没有改变,应用在reselect中,步骤一的运算就可以确定直接缓存运算结果。

  虽然reselect的createSelector创造的选择器并不是一个纯函数,但是createSelector接受的所有函数参数都是纯函数,虽然选择器有“记忆”这个副作用,但是只要输入参数state没有变化,产生的结果也就没有变化,表现得却类似于纯函数。

  只要Redux Store状态树上的filter和todos字段不变,无论怎样触发TodoList的渲染过程,都不会引发没有必要的遍历todos字段的运算,性能自然更快。

  虽然reselect库以re开头,但是逻辑上和React/Redux没有直接关系。实际上,在任何需要这种具有记忆的计算场合都可以使用reselect,不过,对于React和Redux组合的应用,reselect无疑能够提供绝佳的支持。 深入浅出React和Redux

目录
设置
手机
书架
书页
评论