Меня смущают аргументы, которые я передаю в функцию селектора, использующую повторный выбор внутри контейнеров

#reactjs #redux #react-redux #reselect #mapstatetoprops

Вопрос:

Я изучаю использование повторного выбора, но меня смущает то, как я использую запоминаемый селектор внутри моего контейнера.

У меня есть компонент под названием Summary , который получил 3 реквизита, которые есть subtotal, tipAmount , total .

Это выглядит так export const Summary = ({ subtotal = 0, tipAmount = 0, total = 0 })=>{...} .И эти реквизиты вводятся в мое соединение HOC внутри выделенного container , которое я вызвал SummaryContainer со следующим кодом.

 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 селектора selectTipAmount , selectSubtotal и selectTotal я импортирую их из файла селектора с кодом follwing.

 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); внутри mapStateToProps , я total передаю константе результаты выполнения selectTotal(state) selector , и когда я смотрю на argument это, это state .Похоже, я вызываю selectTotal selector аргумент «путем передачи и state «, но в реализации actuel это не функция, которая принимает a state param .В реальной реализации этот выбор равен результату createSelect function , подобному этому:

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

Я не видел, где проходит этот штат argument .Что происходит?

Код работает идеально, но я не понимаю, где state используется аргумент.

Когда я смотрю на selectItems selector это, это имеет смысл, но с селекторами, созданными с createSelector помощью, я теряюсь.

Ответ №1:

Мне проще всего думать о reselect селекторах как о чем-то «противоположном» функциям редуктора redux. С помощью функций редуктора каждый редуктор владеет частью состояния, и когда они объединяются, они образуют дерево редуктора, где, как только они все объединяются, образуют весь state объект.

Селекторы в основном делают обратное. Они начинаются с простых селекторов, которые берут весь state объект целиком и вырывают из него куски. Эти фрагменты вложенных состояний затем могут быть введены в созданные селекторы, чтобы выделить более мелкие фрагменты (или вычислить производное состояние). Используемые входные createSelector данные являются своего рода «зависимостью», когда один из них обновляется, селектор пересчитывает его значение.

createSelector по сути, это функция более высокого порядка, которая использует «зависимости» и функцию результата и возвращает функцию, которая использует state объект. Когда вы звоните selectSubtotal и передаете state , вы действительно переходите state к дереву селектора (или набору деревьев).

Например, при state.items обновлении selectItems селектора, который является входным selectSubtotal , будет обновляться его возвращаемое значение. Это обновление запустится selectSubtotal , чтобы затем пересчитать его значение и так далее для любых других селекторов, которые используются selectSubtotal в качестве входных данных.

Если вам интересно, результирующая функция селектора также будет обладать resultFunc свойством, соответствующим вычислительной функции, первоначально переданной createSelector . Например, selectTipAmount.resultFunc будет (subtotal, tipPercentage) => subtotal * (tipPercentage / 100) , называться как selectTipAmount.result.Func(subTotal, tipPercentage); . Я активно использую это при модульном тестировании своих селекторов, так как меня действительно не волнует запоминание значения, а скорее то, что они правильно вычисляют производное состояние; Мне не нужно будет моделировать весь объект состояния, соответствующий зависимостям селектора.

Ответ №2:

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

Мемоизация-это приятная деталь, которая может сделать ваш код немного более производительным, пропуская виртуальный дом сравниваем но то, что обычно приводит к хаосу на свой спектакль проходит в новый обработчик ссылок, которые вызывают это сравнение, чтобы дать сбой и отреагировать покрасочные, что дом, например <SomeComponent onClick={()=>newRefCreated} /> , будут снова краски, что SomeComponent создает, потому что onClick меняется каждый раз. Вот почему существует крюк обратного вызова.

Я бы сказал, что сила createSelector заключается в простоте повторного использования кода и предотвращении дублирования реализации. Если в приведенном выше примере state.one изменится на строку, мне нужно будет только реорганизовать selectOne функцию, и все остальные селекторы в зависимости от state.one этого все равно будут работать.