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 }