2017-04-14 21 views
0

我從reselect庫中獲得了以下代碼。'createSelector'如何在'reselect'庫中接受輸入參數?

subtotalSelectorexampleState一起被調用時,它將調用接受輸入參數exampleState的函數createSelector

我的問題是關於createSelector如何接受exampleState和其他功能消耗它?有一些我不明白的參數的隱式注入。

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 

subtotalSelector通過替換輸入參數更容易解釋。

subtotalSelector = createSelector(
    state => state.shop.items, 
    items => items.reduce((acc, item) => acc + item.value, 0) 
) 

subtotalSelector({ 
    shop: { 
    taxPercent: 8, 
    items: [ 
     { name: 'apple', value: 1.20 }, 
     { name: 'orange', value: 0.95 }, 
    ] 
    } 
}); 

回答

0

我沒有找到更好的方法來剖析這個reselect library code,但要加console.log的功能裏面。以下代碼是從reselect庫本身複製的。 JSBin版本可以找到here

在控制檯輸出的底部,您可以看到exampleState實際上是代碼中使用的arguments變量。它是一個JavaScript construct

function defaultEqualityCheck(a, b) { 
 
    return a === b 
 
} 
 

 
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. 
 
    const length = prev.length 
 
    for (let i = 0; i < length; i++) { 
 
    if (!equalityCheck(prev[i], next[i])) { 
 
     return false 
 
    } 
 
    } 
 

 
    return true 
 
} 
 

 
function defaultMemoize(func, equalityCheck = defaultEqualityCheck) { 
 
    
 
    let lastArgs = null 
 
    let lastResult = null 
 
    
 
    console.log("Entering defaultMemoize"); 
 
    
 
    console.log("###INPUT### defaultMemoize argument func type: " + typeof func); 
 
    
 
    // 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 
 
} 
 

 
function createSelectorCreator(memoize, ...memoizeOptions) { 
 
    
 
    console.log("Entering createSelectorCreator"); 
 
    
 
    console.log("#INPUT# argument memoize name: " + memoize.name); 
 

 
    console.log("#INPUT# argument memoize options: "); 
 
    
 
    console.log(memoizeOptions); 
 

 
    return (...funcs) => { 
 
    
 
    let recomputations = 0 
 
    
 
    const resultFunc = funcs.pop() 
 
    const dependencies = getDependencies(funcs) 
 

 
    console.log("##INPUT## argument funcs: "); 
 
    console.log(resultFunc); 
 
    
 
    const memoizedResultFunc = memoize(
 
     function() { 
 
     recomputations++ 
 
     
 
     // apply arguments instead of spreading for performance. 
 
     return resultFunc.apply(null, arguments) 
 
     }, 
 
     ...memoizeOptions 
 
    ) 
 
    
 
    console.log("memoizedResultFunc: " + typeof memoizedResultFunc); 
 

 
    // If a selector is called with the exact same arguments we don't need to traverse our dependencies again. 
 
    const selector = defaultMemoize(function() { 
 
     const params = [] 
 
     const length = dependencies.length 
 

 
     if (arguments != null) 
 
     { 
 
     console.log("***INPUT*** arguments: "); 
 
     console.log(arguments); 
 
     } 
 
     
 
     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.recomputations =() => recomputations 
 
    selector.resetRecomputations =() => recomputations = 0 
 
    
 
    return selector 
 
    } 
 
} 
 

 
const createSelector = createSelectorCreator(defaultMemoize) 
 

 
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 
 
     }, {}) 
 
    } 
 
) 
 
} 
 

 
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) 
 
) 
 

 
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 }

下面的代碼示出了類似於上述功能的組合物的一個例子。

function firstFunction() { 
 
    console.log(arguments); 
 
} 
 

 
function secondFunction() { 
 
    console.log(arguments); 
 
} 
 

 
function thirdFunction() { 
 
    console.log(arguments); 
 
} 
 

 
function fourthFunction() { 
 
    console.log(arguments); 
 
    
 
    return function() { 
 
    return function(x) { console.log(arguments); }; 
 
    } 
 
} 
 

 
const higherOrderFunction = fourthFunction(thirdFunction); 
 

 
console.log("High Order Function"); 
 
console.log(higherOrderFunction); 
 

 
const highestOrderFunction = higherOrderFunction(firstFunction, secondFunction); 
 

 
console.log("Highest Order Function"); 
 
console.log(highestOrderFunction); 
 

 
highestOrderFunction(10);