Selectors bug or my misunderstanding
akelmanson opened this issue · 4 comments
Am I doing something wrong?
it('lets the creators access the selectors', () => {
const duck = new Duck({
selectors: {
sum: numbers => numbers.reduce((sum, n) => sum + n, 0)
},
creators: ({ selectors }) => ({
calculate: () => dispatch => {
dispatch({ type: 'CALCULATE', payload: selectors.sum([ 1, 2, 3 ]) })
}
})
})
const dispatch = sinon.spy()
duck.creators.calculate()(dispatch)
expect(dispatch).to.have.been.calledWith({ type: 'CALCULATE', payload: 6 })
})
Result:
1) Duck constructor lets the creators access the selectors:
TypeError: numbers.reduce is not a function
at Object.sum (test/unit/extensible-duck.js:73:35)
at src/extensible-duck.js:98:22
at Array.forEach (native)
at deriveSelectors (src/extensible-duck.js:96:26)
at new Duck (src/extensible-duck.js:132:21)
at Context.<anonymous> (test/unit/extensible-duck.js:71:20)
https://github.com/investtools/extensible-duck/blob/master/src/extensible-duck.js#L98
@akelmanson,, selectors needs initial or some state(defaulted to empty object) for them to be composed,, also selectors should be handled in a way that either it gives undefined or the intended state slice.
https://github.com/investtools/extensible-duck/blob/master/src/extensible-duck.js#L98
If some other reducer agnostic state or some deep state is involved(Since usually its redux store state), i put a defensive check in my selectors
state => state.numbers && state.numbers.map(/logic/)
const duck = new Duck({
initialState: [], // or [1,2,3]
selectors: {
sum: numbers => numbers.reduce((sum, n) => sum + n, 0)
},
creators: ({ selectors }) => ({
calculate: () => dispatch => {
dispatch({ type: 'CALCULATE', payload: selectors.sum([ 1, 2, 3 ]) })
}
})
})
OK, got it!
Anyway, what do you think of this change?
function deriveSelectors(selectors, Duck) {
const composedSelectors = {}
Object.keys(selectors).forEach(key => {
const originalSelector = selectors[key]
composedSelectors[key] = (...args) => {
const result = originalSelector(...args)
return isFunction(result) ? originalSelector(selectors)(...args) : result
}
})
return composedSelectors
}
@akelmanson: This looks neat, but one question,
return isFunction(result) ? originalSelector(selectors)(...args) : result
Here the shouldn't we pass the composedSelectors Object, since selectors still has raw functions that need previous selectors ? Haven't tested it tho, been busy with some travel. Will check once I get free,, meanwhile let me know your thoughts.
I've refactored the selectors implementation in order to avoid the defensive check obligation.
Please check out https://github.com/investtools/extensible-duck#defining-the-selectors