Use reselect selector with parameters

ecmascript-6, javascript, reactjs, redux, reselect

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

Problem

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`

Original source