Search code examples
javascriptreactjsecmascript-6reduxreselect

Use reselect selector with parameters


How do I pass additional parameters to combined selectors? I am trying to

• Get data

• Filter data

• Add custom value to my data set / group data by myValue

export const allData = state => state.dataTable
export const filterText = state => state.filter.get('text')

export const selectAllData = createSelector(
  allData,
  (data) => data
)

export const selectAllDataFiltered = createSelector(
  [ selectAllData, filterText ],
  (data, text) => {
    return data.filter(item => {
      return item.name === text
    })
  }
)

export const selectWithValue = createSelector(
  [ selectAllDataFiltered ],
  (data, myValue) => {
    console.log(myValue)
    return data
  }
)

let data = selectWithValue(state, 'myValue')

console.log(myValue) returns undefined


Solution

  • Updated: 4 December 2023

    No changes compared to 4.1, but the updated document of version 5.0.0 using Typescript. Read document for more detail about the exceptional case.

    // selector.ts
    const selectAvailableItems = createSelector(
      [
        // First input selector extracts items from the state
        (state: RootState) => state.items,
        // Second input selector forwards the category argument
        (state: RootState, category: string) => category,
        // Third input selector forwards the ID argument
        (state: RootState, category: string, id: number) => id
      ],
      // Output selector uses the extracted items, category, and ID
      (items, category, id) =>
        items.filter(item => item.category === category && item.id !== id)
    )
    
    interface RootState {
      items: {
        id: number
        category: string
        vendor: { id: number; name: string }
      }[]
      // ... other state properties ...
    }
    
    // App.tsx
    const items = selectAvailableItems(state, 'javascript', 10);
    // Another way if you're using redux hook:
    const items = useSelector(state => selectAvailableItems(state, 'javascript', 10));
    
    

    Updated: 16 February 2022

    New Solution from Reselect 4.1: See detail

    // selector.js
    const selectItemsByCategory = createSelector(
      [
        // Usual first input - extract value from `state`
        state => state.items,
        // Take the second arg, `category`, and forward to the output selector
        (state, category) => category
      ],
      // Output selector gets (`items, category)` as args
      (items, category) => items.filter(item => item.category === category)
    );
    
    // App.js
    const items = selectItemsByCategory(state, 'javascript');
    // Another way if you're using redux hook:
    const items = useSelector(state => selectItemsByCategory(state, 'javascript'));
    

    Updated: 6 March 2021

    Solution from Reselect: See detail

    // selector.js
    import { createSelector } from 'reselect'
    import memoize from 'lodash.memoize'
    
    const expensiveSelector = createSelector(
      state => state.items,
      items => memoize(
        minValue => items.filter(item => item.value > minValue)
      )
    )
    
    // App.js
    const expensiveFilter = expensiveSelector(state)
    // Another way if you're using redux:
    // const expensiveFilter = useSelector(expensiveSelector)
    
    const slightlyExpensive = expensiveFilter(100)
    const veryExpensive = expensiveFilter(1000000)
    

    Old:

    This is my approach. Creating a function with parameters and return function of reselect.

    export const selectWithValue = (CUSTOM_PARAMETER) => createSelector(
      selectAllDataFiltered,
      (data) => {
        console.log(CUSTOM_PARAMETER)
        return data[CUSTOM_PARAMETER]
      }
    )
    
    const data = selectWithValue('myValue')(myState);