「selector」是一個簡單的Redux庫,靈感來源於
NuclearJS
.html
//這個例子沒必要太在乎,後面會有詳細的介紹
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 }
複製代碼
Installationreact
實例git
APIgithub
FAQtypescript
Licensejson
npm install reselect
redux
實例是基於 Redux Todos List example.設計模式
containers/VisibleTodoList.js
api
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代替getVisibleTodos
,若是state.todos
和state.visibilityFilter
發生變化,他會從新計算state
,可是隻發生在其餘部分的state變化,就不會從新計算.
Reslect提供一個函數createSelector
來建立一個記憶selectors.createSelector
接受input-selectors
和一個變換函數做爲參數.若是Redux的state發生改變形成input-selector
的值發生改變,selector會調用變換函數,依據input-selector
作參數,返回一個結果.若是input-selector
返回的結果和前面的同樣,那麼就會直接返回有關state,會省略變換函數的調用.
下面咱們定義一個記憶selectorgetVisibleTodos
替代非記憶的版本
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.
一個記憶性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
)
)
複製代碼
若是你正在使用 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
複製代碼
這一部分咱們假設程序將會有一個擴展,咱們容許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
//⚠️下面的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每次都會從新來計算而不是返回緩存的值.下一部分咱們將會介紹怎麼解決這個問題.
這一部分的實例須要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
複製代碼
接受一個或者多個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傳遞的參數.這是createSelector
使用的記憶函數.
defaultMemoize
經過調用equalityCheck
函數來決定一個參數是否已經發生改變.由於defaultMemoize
設計出來就是和immutable數據一塊兒使用,默認的equalityCheck
使用引用全等於來判斷變化:
function defaultEqualityCheck(currentVal, previousVal) {
return currentVal === previousVal
}
複製代碼
defaultMemoize
和createSelectorCreator
去配置equalityCheck
函數.
createSelectorCreator
用來配置定製版本的createSelector
.
memoize
參數是一個有記憶功能的函數,來代替defaultMemoize
. …memoizeOption
展開的參數是0或者更多的配置選項,這些參數傳遞給memoizeFunc
.selectorsresultFunc
做爲第一個參數傳遞給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
函數能夠提高便利性.傳遞到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
})
})
複製代碼
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
代替默認的記憶函數,而且使用不一樣的等值檢測函數.請參看這裏 和 這裏做爲參考.
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
單純對象的緣由.
A:能夠.Reselect沒有其餘任何的依賴包,所以儘管他設計的和Redux比較搭配,可是獨立使用也是能夠的.目前的版本在傳統的Flux APP下使用是比較成功的.
若是你使用
createSelector
建立的selectors,須要確保他的參數是immutable的.
看這裏
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)
複製代碼
A: 我認爲這個記憶韓式工做的還能夠,可是若是你須要一個其餘的韓式也是能夠的. 能夠看看這個例子
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.
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
來減小沒必要要的重計算過程.
A: 使用createSelector
建立的Selector的緩存的大小隻有1.這個設定使得多個組件的實例之間的參數不一樣,跨組件共享selector變得不合適.這裏也有幾種辦法來解決這個問題:
A: 是的!他們包含在package.json
裏.能夠很好的工做.
A:嘗試一些這裏助手函數,由MattSPalmer提供
由於Reselect不可能保證緩存你全部的需求,在作很是昂貴的計算的時候,這個方法比較有用.查看一下reselect-maps readme
reselect-map的優化措施僅僅使用在一些小的案例中,若是你不肯定是否是須要他,就不要使用它.
MIT