2

我正在学习重新选择的用法,但我对使用容器内的记忆选择器的方式感到困惑。

我有一个名为的组件Summary,它收到了 3 个道具subtotal, tipAmount , total

看起来像这样export const Summary = ({ subtotal = 0, tipAmount = 0, total = 0 })=>{...}。这些道具正在将我的连接注入到我用以下代码调用HOC的专用设备中。containerSummaryContainer

import { connect } from 'react-redux';
import { Summary } from '../components/Summary';
import {
  selectTipAmount,
  selectSubtotal,
  selectTotal
} from '../store/items/selectors';

const mapStateToProps = (state) => {
  const subtotal = selectSubtotal(state);

  const tipAmount = selectTipAmount(state);
  const total = selectTotal(state);

  return {
    subtotal,
    tipAmount,
    total
  };
};

export const SummaryContainer = connect(mapStateToProps)(Summary);

如您所见,此代码使用 3 个选择器selectTipAmountselectSubtotal并且selectTotal我正在使用以下代码从选择器文件中导入。

import { createSelector } from 'reselect';

const selectItems = (state) => state.items;
const selectTipPercentage = (state) => state.tipPercentage;


export const selectSubtotal = createSelector([selectItems], (items) =>
  items.reduce((acc, { price, quantity }) => acc + price * quantity, 0)
);

export const selectTipAmount = createSelector(
  [selectSubtotal, selectTipPercentage],
  (subtotal, tipPercentage) => subtotal * (tipPercentage / 100)
);

export const selectTotal = createSelector(
  [selectSubtotal, selectTipAmount],
  (subtotal, tipAmount) => subtotal + tipAmount
);


export const selectItemWithTotal = createSelector([selectItems], (items) =>
  items.map((item) => ({ ...item, total: item.price * item.quantity }))
);


export const selectItem = (state, props) =>
  state.items.find((item) => item.uuid === props.uuid);

export const selectItemTotal = createSelector(
  [selectItem],
  (item) => item.price * item.quantity
);

所以这是我的问题:

const total = selectTotal(state);当我在里面调用 write时,mapStateToProps我将执行的结果传递给total常量,selectTotal(state) selector当我查看 时argument,它是 。state看起来我正在selectTotal selector通过传递和state参数调用需要一个state param。在实际实现中,选择等于这样的结果createSelect function

export const selectTotal = createSelector(
  [selectSubtotal, selectTipAmount],
  (subtotal, tipAmount) => subtotal + tipAmount
);

我还没有看到状态在哪里argument通过。这是怎么回事?

该代码运行良好,但我不知道该state参数的使用位置。

当我看selectItems selector它时,它是有道理的,但是用我创建的选择器createSelector我迷路了。

4

2 回答 2

3

我发现最容易将reselect选择器视为 redux reducer 函数的“对立面”。使用 reducer 函数,每个 reducer 都拥有一块状态,当它们组合在一起时,它们会形成一个 reducer 树,一旦它们全部组合在一起就形成了整个 state对象。

选择器基本上是在做相反的事情。他们从简单的选择器开始,这些选择器获取整个 state对象并从中提取出部分。然后可以将这些子状态块馈送到创建的选择器中以提取更小的块(或计算派生状态)。中使用的输入createSelector是一种“依赖”,当其中一个更新时,选择器会重新计算其值。

createSelector本质上是一个高阶函数,它消耗“依赖”和一个结果函数,并返回一个消耗state对象的函数。当您调用selectSubtotal并传递时,state您实际上是传递state到选择器树(或树集)。

例如,当state.items更新selectItems作为输入的选择器时,selectSubtotal将更新其返回值。此更新将触发selectSubtotal然后重新计算其值,以此类推,用于任何其他selectSubtotal用作输入的选择器。

如果您好奇,生成的选择器函数还将具有resultFunc与最初传递给的计算函数相匹配的属性createSelector。例如,selectTipAmount.resultFuncwill be (subtotal, tipPercentage) => subtotal * (tipPercentage / 100),称为 like selectTipAmount.result.Func(subTotal, tipPercentage);。我在对选择器进行单元测试时大量使用它,因为我真的不关心记忆值,而是它们正确计算派生状态;我不需要模拟与选择器依赖项匹配的整个状态对象。

于 2021-05-20T07:38:06.490 回答
1

createSelector函数返回一个函数,因此const mySelectorFunction = createSelector...mySelectorFunciton是一个函数。

没有记忆的简单实现createSelector是:

const createSelector = (functions=[],calculatorFunction) => {
  // you could do memoize(calculatorFunction) to implement memoization
  //the function returned is the selectOnePlusTwo in demo below
  return (...args) =>
    // when this function is called then it will call functions with args
    //  and pass the returning values to calculatorFunction
    calculatorFunction(...functions.map(fn=>fn(...args)))
}
// demo createSelector
const state = { one:1,two:2 }
const selectOne = state=>state.one
const selectTwo =  state=>state.two
const selectOnePlusTwo = createSelector(
  [selectOne,selectTwo],
  (one,two)=>one+two
)
console.log(
  'one plus two:',
  selectOnePlusTwo(state)
)

记忆化是一种很好的接触,它可以通过跳过虚拟 DOM 比较来使您的代码稍微提高性能,但通常会导致性能严重破坏的是传入一个新的处理程序引用,这将导致此比较失败并使 React 重新绘制该 DOM,例如<SomeComponent onClick={()=>newRefCreated} />将重新绘制任何SomeComponent生成的内容,因为onClick每次都会发生变化。这就是为什么有一个useCallback钩子的原因。

我想说 的力量createSelector来自易于重用的代码和防止重复实现。如果在上面的示例state.one中将更改为字符串,我只需要重构selectOne函数,并且所有其他依赖于的选择器state.one仍然可以工作。

于 2021-05-20T18:10:43.283 回答