Reselect
在了解Reselect 之前需要了解一下记忆函数
所谓记忆函数,即函数能够记住最后一次调用的值和结果,如果下一次调用参数无变化,就返回上一次的结果。
function memoize(f) {
    var cache = {};
    return function(){
        var key = arguments.length + Array.prototype.join.call(arguments, ",");
        if (key in cache) {
            return cache[key]
        }
        else return cache[key] = f.apply(this, arguments)
    }
}
Reselect 最核心的作用就是缓存记忆。
Reselect的功能
- Selectors可以计算派生数据,允许Redux存储最小可能状态。
- Selectors不会重新计算除非参数发生了变化
- Selectors是可以组合的,可以作为其他Selectors的输入
Reselect 基本用法
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { toggleTodo } from '../actions';
import TodoList from '../components/TodoList';
const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos
export const getVisibleTodos = createSelector(
  [ getVisibilityFilter, getTodos ],
  (visibilityFilter, todos) => {
    switch (visibilityFilter) {
      case 'SHOW_ALL':
        return todos
      case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed)
      case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed)
    }
  }
)
const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state)
  }
}
默认相等检查函数
采用的是全等比较
function defaultEqualityCheck(a, b) {
  return a === b
}
参数比较
- equalityCheck 比较函数
- prev 前一个值
- next 下一个值 - function areArgumentsShallowlyEqual(equalityCheck, prev, next) { if (prev === null || next === null || prev.length !== next.length) { return false } // Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible. // 使用for循环而不是every或forEach,可以尽快地进行相等判断,发现不等可以提前结束循环 const length = prev.length for (let i = 0; i < length; i++) { if (!equalityCheck(prev[i], next[i])) { return false } } return true }
defaultMemoize
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
  let lastArgs = null;// 上一次调用的参数
  let lastResult = null;// 上一次调用的结果
  // we reference arguments instead of spreading them for performance reasons
  return function () {
    //比较两次调用参数是否相同
    if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
      // apply arguments instead of spreading for performance.
      lastResult = func.apply(null, arguments)
    }
    lastArgs = arguments
    return lastResult
  }
}
`
依赖函数校验器
检查参数确保每个元素均是函数 第一个参数可以是一个数组
function getDependencies(funcs) {
  const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs
  if (!dependencies.every(dep => typeof dep === 'function')) {
    const dependencyTypes = dependencies.map(
      dep => typeof dep
    ).join(', ')
    throw new Error(
      'Selector creators expect all input-selectors to be functions, ' +
      `instead received the following types: [${dependencyTypes}]`
    )
  }
  return dependencies
}
createSelectorCreator 选择器函数生成器
createSelectorCreator 接受一个默认的缓存函数,返回一个函数用于接受选择器
所谓的选择器就是接受state作为参数的函数,其返回值是mapStateToProps需要的结果
Reselect把mapStateToProps的工作分为两部分
- 从输入参数state抽取第一层结果,将这个结果和之前的比较,如果相同就直接返回之前的结果
- 如果不同就重新计算
export function createSelectorCreator(memoize, ...memoizeOptions) {
  return (...funcs) => {
    let recomputations = 0; //统计是计算次数
    const resultFunc = funcs.pop(); //结果函数
    const dependencies = getDependencies(funcs) //依赖函数
    //将resultFunc 包装成记忆函数
    const memoizedResultFunc = memoize(
      function () {
        recomputations++
        // apply arguments instead of spreading for performance.
        return resultFunc.apply(null, arguments)
      },
      ...memoizeOptions
    )
    // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
    const selector = memoize(function () {
      const params = []// 要记忆的映射函数计算结果
      const length = dependencies.length
      for (let i = 0; i < length; i++) {
        // apply arguments instead of spreading and mutate a local list of params for performance.
        params.push(dependencies[i].apply(null, arguments))
      }
      // apply arguments instead of spreading for performance.
      // 把映射函数的计算结果传给步骤二的结果计算函数
      return memoizedResultFunc.apply(null, params)
    })
    selector.resultFunc = resultFunc
    selector.dependencies = dependencies
    selector.recomputations = () => recomputations
    selector.resetRecomputations = () => recomputations = 0
    return selector
  }
}
export const createSelector = createSelectorCreator(defaultMemoize)
createStructuredSelector
createStructuredSelector 的inputSelectors是一个对象,返回一个结构化的selector。该对象具有与inputSelectors参数相同的键,但是其结果是选择器执行的结果
export function createStructuredSelector(selectors, selectorCreator = createSelector) {
  if (typeof selectors !== 'object') {
    throw new Error(
      'createStructuredSelector expects first argument to be an object ' +
      `where each property is a selector, instead received a ${typeof selectors}`
    )
  }
  const objectKeys = Object.keys(selectors)
  return selectorCreator(
    objectKeys.map(key => selectors[key]),
    (...values) => {
      return values.reduce((composition, value, index) => {
        composition[objectKeys[index]] = value
        return composition
      }, {})
    }
  )
}
example:
const mySelectorA = state => state.a
const mySelectorB = state => state.b
const structuredSelector = createStructuredSelector({
  x: mySelectorA,
  y: mySelectorB
})
const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 }