使用带参数的重选选择器

Mic*_*hal 19 javascript ecmascript-6 reactjs redux reselect

如何将其他参数传递给组合选择器?我在尝试着

•获取数据

•过滤数据

•通过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')
Run Code Online (Sandbox Code Playgroud)

console.log(myValue) 回报 undefined

Col*_*lCh 8

从选择器返回一个函数怎么样?getFilteredToDos是一个例子

// redux part
const state = {
  todos: [
    { state: 'done',     text: 'foo' },
    { state: 'time out', text: 'bar' },
  ],
};

// selector for todos
const getToDos = createSelector(
  getState,
  (state) => state.todos,
);

// selector for filtered todos
const getFilteredToDos = createSelector(
  getToDos,
  (todos) => (todoState) => todos.filter((toDo) => toDo.state === todoState);
);

// and in component
const mapStateToProps = (state, ownProps) => ({
  ...ownProps,
  doneToDos: getFilteredToDos()('done')
});
Run Code Online (Sandbox Code Playgroud)

  • 不过,这可能会影响记忆。 (3认同)
  • ...我实际上不认为它会(与下面的答案不同)。每个过滤器没有记忆,但有“getToDos”的记忆。这是在不支持参数化选择器时重新选择可以获得的范围。 (2认同)

Lon*_*yen 8

更新时间:2021 年 3 月 36 日

Reselector的解决方案: 查看详细信息

// 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)
Run Code Online (Sandbox Code Playgroud)

老的:

这是我的方法。创建一个带参数和返回函数的函数reselect

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

const data = selectWithValue('myValue')(myState);
Run Code Online (Sandbox Code Playgroud)

  • 不建议在更改 CUSTOM_PARAMETER 的组件中使用它;否则,它最终会根据以后的使用方式创建选择器的多个副本,这可能会非常昂贵,因为它会记住重复的输入/输出和无限数量的排列,而这些排列可能会被其他任何东西引用。(但是,在您的选择器创建者中可能会很好;只是认为这不是问题所在) (4认同)
  • 您提出的解决方案有效,但它向我抛出此错误“预期 1 个参数,但有 2 个”任何想法吗? (4认同)

Dav*_*lsh 6

您的问题的答案在FAQ中有详细说明:https//github.com/reactjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument

简而言之,重新选择不支持传递给选择器的任意参数。推荐的方法是,将相同的数据存储在Redux状态中,而不是传递参数。

  • 有时候这是不可能的。在这种情况下,您可以将选择器设为接受参数并返回选择器的工厂。然后,您可以在“ mapStateToProps”工厂中“创建”选择器,并使用作用于参数的选择器实例。我看到的这种方法的唯一缺点是,当参数更改时,您必须手动重新创建选择器。 (2认同)
  • 想象一下,您要存储针对待办事项列表使用的搜索查询:getTodosSelector和getQuerySelector,因为即使您两次搜索相同的关键字,该查询也被视为已更改,所以永远不会命中缓存。 (2认同)
  • @EliranMalka当您仅在相关状态/属性实际更改时重新创建选择器时,则不会。否则,在每次渲染中重新创建选择器时,它会破坏记忆。 (2认同)

Bad*_*ibo 6

这是最新的 useSelector钩子的。

重要的是从输入选择器中获取参数。输入选择器的第二个参数是我们获取它的方式。

这是选择器的外观,

const selectNumOfTodosWithIsDoneValue = createSelector(
  (state) => state.todos,
  (_, isDone) => isDone, // this is the parameter we need
  (todos, isDone) => todos.filter((todo) => todo.isDone === isDone).length
)
Run Code Online (Sandbox Code Playgroud)

这是我们如何使用useSelector钩子提取值,

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const NumOfTodosWithIsDoneValue = useSelector((state) =>
    selectNumOfTodosWithIsDoneValue(state, isDone)
  )

  return <div>{NumOfTodosWithIsDoneValue}</div>
}
Run Code Online (Sandbox Code Playgroud)

另外,保留第二个参数 ( isDone) 作为原始值(字符串、数字等)。因为,reselect 仅在输入选择器值更改时运行输出选择器。这种变化是通过浅比较来检查的,对于对象和数组等参考值,这种比较总是错误的。

参考:

  1. https://react-redux.js.org/next/api/hooks#using-memoizing-selectors
  2. https://flufd.github.io/reselect-with-multiple-parameters/
  3. https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/