内容简介:“selector”是一个简单的Redux库,灵感来源于实例是基于
“selector”是一个简单的Redux库,灵感来源于 NuclearJS
.
- Selector可以计算衍生的数据,可以让Redux做到存储尽可能少的state。
- Selector比较高效,只有在某个参数发生变化的时候才发生计算过程.
- Selector是可以组合的,他们可以作为输入,传递到其他的selector.
//这个例子不必太在意,后面会有详细的介绍 import { createSelector } from 'reselect' const shopItemsSelector = state => state.shop.items const taxPercentSelector = state => state.shop.taxPercent const subtotalSelector = createSelector( shopItemsSelector, items => items.reduce((acc, item) => acc + item.value, 0) ) const taxSelector = createSelector( subtotalSelector, taxPercentSelector, (subtotal, taxPercent) => subtotal * (taxPercent / 100) ) export const totalSelector = createSelector( subtotalSelector, taxSelector, (subtotal, tax) => ({ total: subtotal + tax }) ) let exampleState = { shop: { taxPercent: 8, items: [ { name: 'apple', value: 1.20 }, { name: 'orange', value: 0.95 }, ] } } console.log(subtotalSelector(exampleState)) // 2.15 console.log(taxSelector(exampleState)) // 0.172 console.log(totalSelector(exampleState)) // { total: 2.322 } 复制代码
Table of Contents
-
- Motivation for Memoized Selectors
- Creating a Memoized Selector
- Connecting a Selector to the Redux Store
- Accessing React Props in Selectors
- Sharing Selectors with Props Across Multiple Components
-
-
createSelectorCreator
-
createStructuredSelector
-
-
- Why isn't my selector recomputing when the input state changes?
- Why is my selector recomputing when the input state stays the same?
- Can I use Reselect without Redux?
- The default memoization function is no good, can I use a different one?
- How do I test a selector?
- How do I create a selector that takes an argument?
- How do I use Reselect with Immutable.js?
- Can I share a selector across multiple components?
- Are there TypeScript typings?
- How can I make a curried selector?
安装
npm install reselect
实例
缓存Selcectos的动机
实例是基于 Redux Todos List example .
containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' //下面这段代码是根据过滤器的state来改变日程state的函数 const getVisibleTodos = (todos, filter) => { switch (filter) { 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是根据过滤函数返回的state,传入两个实参 todos: getVisibleTodos(state.todos, state.visibilityFilter) } } //mapDispatchToProps来传递dispatch的方法 const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } } //使用Redux的connect函数注入state,到TodoList组件 const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList 复制代码
在上面的例子中, mapStateToProps
调用 getVisibleTodos
去计算 todos
.这个函数设计的是相当好的,但是有个缺点: todos
在每一次组件更新的时候都会重新计算.如果state树的结构比较大,或者计算比较昂贵,每一次组件更新的时候都进行计算的话,将会导致性能问题. Reselect
能够帮助redux避免不必要的计算过程.
创建一个缓存Selector
我们可以使用记忆缓存selector代替 getVisibleTodos
,如果 state.todos
和 state.visibilityFilter
发生变化,他会重新计算 state
,但是只发生在其他部分的state变化,就不会重新计算.
Reslect提供一个函数 createSelector
来创建一个记忆selectors. createSelector
接受 input-selectors
和一个变换函数作为参数.如果Redux的state发生改变造成 input-selector
的值发生改变,selector会调用变换函数,依据 input-selector
做参数,返回一个结果.如果 input-selector
返回的结果和前面的一样,那么就会直接返回有关state,会省略变换函数的调用.
下面我们定义一个记忆selector getVisibleTodos
替代非记忆的版本
selectors/index.js
import { createSelector } from 'reselect' 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) } } ) 复制代码
上面的的实例中, getVisibilityfilter
和 getTodos
是input-selectors.这两个函数是普通的非记忆selector函数,因为他们没有变换他们select的数据. getVisibleTodos
另一方面是一个记忆selector.他接收 getVisibilityfilter
和 getTodos
作为input-selectors,并且作为一个变换函数计算筛选的todo list.
组合selectors
一个记忆性selector本身也可以作为另一个记忆性selector的input-selector.这里 getVisibleTodos
可以作为input-selector作为关键字筛选的input-selector:
const getKeyword = (state) => state.keyword const getVisibleTodosFilteredByKeyword = createSelector( [ getVisibleTodos, getKeyword ], (visibleTodos, keyword) => visibleTodos.filter( todo => todo.text.indexOf(keyword) > -1 ) ) 复制代码
把Selector连接到Redux Store
如果你正在使用 React Redux
, 你可以 直接在 mapStateToProps()
中调用 selector:
containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { getVisibleTodos } from '../selectors' const mapStateToProps = (state) => { return { todos: getVisibleTodos(state) } } const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList 复制代码
在Selectors中获取 React 的 props
这一部分我们假设程序将会有一个扩展,我们允许selector支持多重todo List.请注意如果要完全实施这个扩展,reducers,components,actions等等都需要作出改变.这些内容和主题不是太相关,所以这里就省略掉了.
目前为止,我们仅仅看到selectors接收store的state作为一个参数,其实一个selector叶可以接受props.
这里是一个 App
组件,渲染出三个 VisibleTodoList
组件,每一个组件有 ListId
属性.
components/App.js
import React from 'react' import Footer from './Footer' import AddTodo from '../containers/AddTodo' import VisibleTodoList from '../containers/VisibleTodoList' const App = () => ( <div> <VisibleTodoList listId="1" /> <VisibleTodoList listId="2" /> <VisibleTodoList listId="3" /> </div> ) 复制代码
每一个 VisibleTodoList
container应该根据各自的 listId
属性获取state的不同部分.所以我们修改一下 getVisibilityFilter
和 getTodos
,便于接受一个属性参数
selectors/todoSelectors.js
import { createSelector } from 'reselect' const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter const getTodos = (state, props) => state.todoLists[props.listId].todos //这里是为二维数组了 const getVisibleTodos = createSelector( [ getVisibilityFilter, getTodos ], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } } ) export default getVisibleTodos 复制代码
props
可以从 mapStateToProps
传递到 getVisibleTodos
:
const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } 复制代码
现在 getVisibleTodos
可以获取 props
,每一部分似乎都工作的不错.
** 但是还有个问题!
当 getVisibleTodos
selector和 VisibleTodoList
container的多个实例一起工作的时候,记忆功能就不能正常运行:
containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { getVisibleTodos } from '../selectors' const mapStateToProps = (state, props) => { return { // WARNING: THE FOLLOWING SELECTOR DOES NOT CORRECTLY MEMOIZE //:warning:下面的selector不能正确的记忆 todos: getVisibleTodos(state, props) } } const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList 复制代码
使用 createSelector
创建的selector时候,如果他的参数集合和上一次的参数机会是一样的,仅仅返回缓存的值.如果我们交替渲染 <VisibleTodoList listId="1" />
和 <VisibleTodoList listId="2" />
时,共享的selector将会交替接受 {listId:1}
和 {listId:2}
作为他的props的参数.这将会导致每一次调用的时候的参数都不同,因此selector每次都会重新来计算而不是返回缓存的值.下一部分我们将会介绍怎么解决这个问题.
跨越多个组件使用selectors共享props
这一部分的实例需要React Redux v4.3.0或者更高版本的支持.
在多个 VisibleTodoList
组件中共享selector,同时还要 保持
记忆性,每一个组件的实例需要他们自己的selector私有拷贝.
现在让我们创建一个函数 makeGetVisibleTodos
,这个函数每次调用的时候返回一个新的 getVisibleTodos
的拷贝:
selectors/todoSelectors.js
import { createSelector } from 'reselect' const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter const getTodos = (state, props) => state.todoLists[props.listId].todos const makeGetVisibleTodos = () => { return createSelector( [ getVisibilityFilter, getTodos ], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } } ) } export default makeGetVisibleTodos 复制代码
我们也需要设置给每一个组件的实例他们各自获取私有的selector方法. mapStateToProps
的 connect
函数可以帮助完成这个功能.
如果 mapStateToProps
提供给 connect
的不是一个对形象,而是一个函数,每个 container
中就会创建独立的 mapStateToProps
实例.
在下面的实例中, mapStateProps
创建一个新的 getVisibleTodos
selector,他返回一个 mapStateToProps
函数,这个函数能够接入新的selector.
const makeMapStateToProps = () => { const getVisibleTodos = makeGetVisibleTodos() const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } return mapStateToProps } 复制代码
如果我们把 makeMapStateToprops
传递到 connect
,每一个 visibleTodoList
container将会获得各自的含有私有 getVisibleTodos
selector的 mapStateToProps
函数.这样一来记忆就正常了,不管 VisibleTodoList
containers的渲染顺序怎么样.
containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { makeGetVisibleTodos } from '../selectors' const makeMapStateToProps = () => { const getVisibleTodos = makeGetVisibleTodos() const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } return mapStateToProps } const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( makeMapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList 复制代码
API
createSelector(…inputSelectors|[inputSelectors],resultFunc)
接受一个或者多个selectors,或者一个selectors数组,计算他们的值并且作为参数传递给 resultFunc
.
createSelector
通过判断input-selector之前调用和之后调用的返回值的全等于(===,这个地方英文文献叫reference equality,引用等于,这个单词是本质,中文没有翻译出来).经过 createSelector
创建的selector应该是immutable(不变的).
经过 createSelector
创建的Selectors有一个缓存,大小是1.这意味着当一个input-selector变化的时候,他们总是会重新计算state,因为Selector仅仅存储每一个input-selector前一个值.
const mySelector = createSelector( state => state.values.value1, state => state.values.value2, (value1, value2) => value1 + value2 ) // You can also pass an array of selectors //可以出传递一个selector数组 const totalSelector = createSelector( [ state => state.values.value1, state => state.values.value2 ], (value1, value2) => value1 + value2 ) 复制代码
在selector内部获取一个组件的props非常有用.当一个selector通过 connect
函数连接到一个组件上,组件的属性作为第二个参数传递给selector:
const abSelector = (state, props) => state.a * props.b // props only (ignoring state argument) const cSelector = (_, props) => props.c // state only (props argument omitted as not required) const dSelector = state => state.d const totalSelector = createSelector( abSelector, cSelector, dSelector, (ab, c, d) => ({ total: ab + c + d }) ) 复制代码
defaultMemoize(func, equalityCheck = defaultEqualityCheck)
defaultMemoize
能记住通过func传递的参数.这是 createSelector
使用的记忆函数.
defaultMemoize
通过调用 equalityCheck
函数来决定一个参数是否已经发生改变.因为 defaultMemoize
设计出来就是和immutable数据一起使用,默认的 equalityCheck
使用引用全等于来判断变化:
function defaultEqualityCheck(currentVal, previousVal) { return currentVal === previousVal } 复制代码
defaultMemoize
和 createSelectorCreator
去
配置 equalityCheck
函数 .
createSelectorCreator(memoize,…memoizeOptions)
createSelectorCreator
用来配置定制版本的 createSelector
.
memoize
参数是一个有记忆功能的函数,来代替 defaultMemoize
. …memoizeOption
展开的参数是0或者更多的配置选项,这些参数传递给 memoizeFunc
.selectors resultFunc
作为第一个参数传递给 memoize
, memoizeOptions
作为第二个参数:
const customSelectorCreator = createSelectorCreator( customMemoize, // function to be used to memoize resultFunc,记忆resultFunc option1, // option1 will be passed as second argument to customMemoize 第二个惨呼 option2, // option2 will be passed as third argument to customMemoize 第三个参数 option3 // option3 will be passed as fourth argument to customMemoize 第四个参数 ) const customSelector = customSelectorCreator( input1, input2, resultFunc // resultFunc will be passed as first argument to customMemoize 作为第一个参数传递给customMomize ) 复制代码
在 customSelecotr
内部滴啊用memoize的函数的代码如下:
customMemoize(resultFunc, option1, option2, option3) 复制代码
下面是几个可能会用到的 createSelectorCreator
的实例:
为 defaultMemoize
配置 equalityCheck
import { createSelectorCreator, defaultMemoize } from 'reselect' import isEqual from 'lodash.isEqual' // create a "selector creator" that uses lodash.isEqual instead of === const createDeepEqualSelector = createSelectorCreator( defaultMemoize, isEqual ) // use the new "selector creator" to create a selector const mySelector = createDeepEqualSelector( state => state.values.filter(val => val < 5), values => values.reduce((acc, val) => acc + val, 0) ) 复制代码
使用loadsh的memoize函数来缓存未绑定的缓存.
import { createSelectorCreator } from 'reselect' import memoize from 'lodash.memoize' let called = 0 const hashFn = (...args) => args.reduce( (acc, val) => acc + '-' + JSON.stringify(val), '' ) const customSelectorCreator = createSelectorCreator(memoize, hashFn) const selector = customSelectorCreator( state => state.a, state => state.b, (a, b) => { called++ return a + b } ) 复制代码
createStructuredSelector({inputSelectors}, selectorCreator = createSelector)
如果在普通的模式下使用 createStructuredSelector
函数可以提升便利性.传递到 connect
的selector装饰者(这是js设计模式的概念,可以参考相关的书籍)接受他的input-selectors,并且在一个对象内映射到一个键上.
const mySelectorA = state => state.a const mySelectorB = state => state.b // The result function in the following selector // is simply building an object from the input selectors 由selectors构建的一个对象 const structuredSelector = createSelector( mySelectorA, mySelectorB, mySelectorC, (a, b, c) => ({ a, b, c }) ) 复制代码
createStructuredSelector
接受一个对象,这个对象的属性是input-selectors,函数返回一个结构性的selector.这个结构性的selector返回一个对象,对象的键和 inputSelectors
的参数是相同的,但是使用selectors代替了其中的值.
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 } 复制代码
结构性的selectors可以是嵌套式的:
const nestedSelector = createStructuredSelector({ subA: createStructuredSelector({ selectorA, selectorB }), subB: createStructuredSelector({ selectorC, selectorD }) }) 复制代码
FAQ
Q:为什么当输入的state发生改变的时候,selector不重新计算?
A:检查一下你的记忆韩式是不是和你的state更新函数相兼容(例如:如果你正在使用Redux).例如:使用 createSelector
创建的selector总是创建一个新的对象,原来期待的是更新一个已经存在的对象. createSelector
使用(===)检测输入是否改变,因此如果改变一个已经存在的对象没有触发selector重新计算的原因是改变一个对象的时候没有触发相关的检测.提示:如果你正在使用Redux,改变一个state对象的错误可能有.
下面的实例定义了一个selector可以决定数组的第一个todo项目是不是已经被完成:
const isFirstTodoCompleteSelector = createSelector( state => state.todos[0], todo => todo && todo.completed ) 复制代码
下面的state更新函数和 isFirstTodoCompleteSelector
将不会
正常工作工作:
export default function todos(state = initialState, action) { switch (action.type) { case COMPLETE_ALL: const areAllMarked = state.every(todo => todo.completed) // BAD: mutating an existing object return state.map(todo => { todo.completed = !areAllMarked return todo }) default: return state } } 复制代码
下面的state更新函数和 isFirstTodoComplete
一起 可以
正常工作.
export default function todos(state = initialState, action) { switch (action.type) { case COMPLETE_ALL: const areAllMarked = state.every(todo => todo.completed) // GOOD: returning a new object each time with Object.assign return state.map(todo => Object.assign({}, todo, { completed: !areAllMarked })) default: return state } } 复制代码
如果你没有使用Redux,但是有使用mutable数据的需求,你可以使用 createSelectorCreator
代替默认的记忆函数,并且使用不同的等值检测函数.请参看和作为参考.
Q:为什么input state没有改变的时候,selector还是会重新计算?
A: 检查一下你的记忆函数和你你的state更新函数是不是兼容(如果是使用Redux的时候,看看reducer).例如:使用每一次更新的时候,不管值是不是发生改变, createSelector
创建的selector总是会收到一个新的对象. createSelector
函数使用( ===
)检测input的变化,由此可知如果每次都返回一个新对象,表示selector总是在每次更新的时候重新计算.
import { REMOVE_OLD } from '../constants/ActionTypes' const initialState = [ { text: 'Use Redux', completed: false, id: 0, timestamp: Date.now() } ] export default function todos(state = initialState, action) { switch (action.type) { case REMOVE_OLD: return state.filter(todo => { return todo.timestamp + 30 * 24 * 60 * 60 * 1000 > Date.now() }) default: return state } } 复制代码
下面的selector在每一次REMOVE_OLD调用的时候,都会重新计算,因为Array.filter总是返回一个新对象.但是在大多数情况下,REMOVE_OLD action都不会改变todo列表,所以重新计算是不必要的.
import { createSelector } from 'reselect' const todosSelector = state => state.todos export const visibleTodosSelector = createSelector( todosSelector, (todos) => { ... } ) 复制代码
你可以通过state更新函数返回一个新对象来减少不必要的重计算操作,这个对象执行深度等值检测,只有深度不相同的时候才返回新对象.
import { REMOVE_OLD } from '../constants/ActionTypes' import isEqual from 'lodash.isEqual' const initialState = [ { text: 'Use Redux', completed: false, id: 0, timestamp: Date.now() } ] export default function todos(state = initialState, action) { switch (action.type) { case REMOVE_OLD: const updatedState = state.filter(todo => { return todo.timestamp + 30 * 24 * 60 * 60 * 1000 > Date.now() }) return isEqual(updatedState, state) ? state : updatedState default: return state } } 复制代码
替代的方法是,在selector中使用深度检测方法替代默认的 equalityCheck
函数:
import { createSelectorCreator, defaultMemoize } from 'reselect' import isEqual from 'lodash.isEqual' const todosSelector = state => state.todos // create a "selector creator" that uses lodash.isEqual instead of === const createDeepEqualSelector = createSelectorCreator( defaultMemoize, isEqual ) // use the new "selector creator" to create a selector const mySelector = createDeepEqualSelector( todosSelector, (todos) => { ... } ) 复制代码
检查 equalityCheck
函数的更替或者在state更新函数中做深度检测并不总是比重计算的花销小.如果每次重计算的花销总是比较小,可能的原因是Reselect没有通过 connect
函数传递 mapStateProps
单纯对象的原因.
Q:没有Redux的情况下可以使用Reselect吗?
A:可以.Reselect没有其他任何的依赖包,因此尽管他设计的和Redux比较搭配,但是独立使用也是可以的.目前的版本在传统的Flux APP下使用是比较成功的.
如果你使用 createSelector
创建的selectors,需要确保他的参数是immutable的.
看
Q:怎么才能创建一个接收参数的selector.
A:Reselect没有支持创建接收参数的selectors,但是这里有一些实现类似函数功能的建议.
如果参数不是动态的,你可以使用工厂函数:
const expensiveItemSelectorFactory = minValue => { return createSelector( shopItemsSelector, items => items.filter(item => item.value > minValue) ) } const subtotalSelector = createSelector( expensiveItemSelectorFactory(200), items => items.reduce((acc, item) => acc + item.value, 0) ) 复制代码
总的达成共识 看这里 和 超越 neclear-js 是:如果一个selector需要动态的参数,那么参数应该是store中的state.如果你决定好了在应用中使用动态参数,像下面这样返回一个记忆函数是比较合适的:
import { createSelector } from 'reselect' import memoize from 'lodash.memoize' const expensiveSelector = createSelector( state => state.items, items => memoize( minValue => items.filter(item => item.value > minValue) ) ) const expensiveFilter = expensiveSelector(state) const slightlyExpensive = expensiveFilter(100) const veryExpensive = expensiveFilter(1000000) 复制代码
Q:默认的记忆函数不太好,我能用个其他的吗?
A: 我认为这个记忆韩式工作的还可以,但是如果你需要一个其他的韩式也是可以的. 可以看看这个
Q:怎么才能测试一个selector?
A:对于一个给定的input,一个selector总是产出相同的结果.基于这个原因,做单元测试是非常简单的.
const selector = createSelector( state => state.a, state => state.b, (a, b) => ({ c: a * 2, d: b * 3 }) ) test("selector unit test", () => { assert.deepEqual(selector({ a: 1, b: 2 }), { c: 2, d: 6 }) assert.deepEqual(selector({ a: 2, b: 3 }), { c: 4, d: 9 }) }) 复制代码
在state更新函数调用的时候同时检测selector的记忆函数的功能也是非常有用的(例如 使用Redux的时候检查reducer).每一个selector都有一个 recomputations
方法返回重新计算的次数:
suite('selector', () => { let state = { a: 1, b: 2 } const reducer = (state, action) => ( { a: action(state.a), b: action(state.b) } ) const selector = createSelector( state => state.a, state => state.b, (a, b) => ({ c: a * 2, d: b * 3 }) ) const plusOne = x => x + 1 const id = x => x test("selector unit test", () => { state = reducer(state, plusOne) assert.deepEqual(selector(state), { c: 4, d: 9 }) state = reducer(state, id) assert.deepEqual(selector(state), { c: 4, d: 9 }) assert.equal(selector.recomputations(), 1) state = reducer(state, plusOne) assert.deepEqual(selector(state), { c: 6, d: 12 }) assert.equal(selector.recomputations(), 2) }) }) 复制代码
另外,selectors保留了最后一个函数调用结果的引用,这个引用作为 .resultFunc
.如果你已经聚合了其他的selectors,这个函数引用可以帮助你测试每一个selector,不需要从state中解耦测试.
例如如果你的selectors集合像下面这样:
selectors.js
export const firstSelector = createSelector( ... ) export const secondSelector = createSelector( ... ) export const thirdSelector = createSelector( ... ) export const myComposedSelector = createSelector( firstSelector, secondSelector, thirdSelector, (first, second, third) => first * second < third ) 复制代码
单元测试就像下面这样: test/selectors.js
// tests for the first three selectors... test("firstSelector unit test", () => { ... }) test("secondSelector unit test", () => { ... }) test("thirdSelector unit test", () => { ... }) // We have already tested the previous // three selector outputs so we can just call `.resultFunc` // with the values we want to test directly: test("myComposedSelector unit test", () => { // here instead of calling selector() // we just call selector.resultFunc() assert(selector.resultFunc(1, 2, 3), true) assert(selector.resultFunc(2, 2, 1), false) }) 复制代码
最后,每一个selector有一个 resetRecomputations
方法,重置recomputations方法为0,这个参数的意图是在面对复杂的selector的时候,需要很多独立的测试,你不需要管理复杂的手工计算,或者为每一个测试创建”傻瓜”selector.
Q:Reselect怎么和Immutble.js一起使用?
A: creatSelector
创建的Selectors应该可以和Immutable.js数据结构一起完美的工作.
如果你的selector正在重计算,并且你认为state没有发生变化,一定要确保知道哪一个Immutable.js更新方法,这个方法只要一更新 总是
返回新对象.哪一个方法只有 集合实际发生变化的时候
才返回新对象.
import Immutable from 'immutable' let myMap = Immutable.Map({ a: 1, b: 2, c: 3 }) // set, merge and others only return a new obj when update changes collection let newMap = myMap.set('a', 1) assert.equal(myMap, newMap) newMap = myMap.merge({ 'a', 1 }) assert.equal(myMap, newMap) // map, reduce, filter and others always return a new obj newMap = myMap.map(a => a * 1) assert.notEqual(myMap, newMap) 复制代码
如果一个操作导致的selector更新总是返回一个新对象,可能会发生不必要的重计算..这是一个关于pros的讨论,使用深全等于来检测例如 immutable.js
来减少不必要的重计算过程.
Q:可以在多个组件之间共享selector吗?
A: 使用 createSelector
创建的Selector的缓存的大小只有1.这个设定使得多个组件的实例之间的参数不同,跨组件共享selector变得不合适.这里也有几种办法来解决这个问题:
- 使用工程函数方法,为每一个组件实例创建一个新的selector.这里有一个内建的工厂方法,React Redux v4.3或者更高版本可以使用.
- 创建一个缓存尺寸大于1的定制selector.
Q:有TypeScript的类型吗?
A: 是的!他们包含在 package.json
里.可以很好的工作.
Q:怎么构建一个 柯里化 selector?
A:尝试一些这里 助手函数 ,由 MattSPalmer 提供
有关的项目
reselect-map
因为Reselect不可能保证缓存你所有的需求,在做 非常昂贵的计算 的时候,这个方法比较有用.查看一下reselect-maps readme
reselect-map的优化措施仅仅使用在一些小的案例中,如果你不确定是不是需要他,就不要使用它.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- go-carbon 1.3.2 版本发布,新增自定义 i18n 方法和日文翻译文件
- 基于 Laravel、Lumen 框架集成百度翻译、有道翻译、Google 翻译扩展包
- 腾讯发布人工智能辅助翻译 致敬人工翻译
- golang调用baidu翻译api实现自动翻译
- 监管机器翻译质量?且看阿里如何搭建翻译质量评估模型
- 机器翻译新突破:谷歌实现完全基于attention的翻译架构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。