#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
меняется каждый раз. Вот почему существует крюк обратного вызова.