I'm learning the usage of reselect, but i'm confused about the way i make use of the memoized selector in inside my container.
I've got a component called Summary
that received 3 props thats are subtotal, tipAmount , total
.
It looks like this export const Summary = ({ subtotal = 0, tipAmount = 0, total = 0 })=>{...}
.And those props are being inject my the connect HOC
inside a dedicated container
that i've called SummaryContainer
with the following code.
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);
As you can see this code uses 3 selectors selectTipAmount
, selectSubtotal
and selectTotal
that i'm importing from a selector file with the follwing code.
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
);
So here is my problem :
When i call write const total = selectTotal(state);
inside mapStateToProps
i passe to the total
constant the results of the execution of selectTotal(state) selector
and when i look at the argument
, it's state
.It looks like i'm invoking the selectTotal selector
by passing and state
argument, but in the actuel implementation it isn't a function that takes a state param
.In the actual implementation that selection equals the result of createSelect function
like this:
export const selectTotal = createSelector(
[selectSubtotal, selectTipAmount],
(subtotal, tipAmount) => subtotal + tipAmount
);
I haven't seen where the state argument
is being passed.What is going on?
The code works perfectly but i'm lost about where the state
argument is being used.
When i look at selectItems selector
it makes sens, but with selectors that are created with createSelector
i'm lost.
I find it easiest to think of
reselect
selectors as sort of the "opposite" of redux reducer functions. With reducer functions each reducer owns a chunk of state and when they are combined they form a reducer tree where once they are all combined form the entirestate
object.The selectors are basically doing the opposite. They start with simple selectors that take the entire
state
object and pluck out chunks of it. These sub-state chunks can then be fed into created selectors to pluck out smaller chunks (or compute derived state). The inputs used increateSelector
are a sort of "dependency", when one of them updates then the selector recomputes its value.createSelector
is essentially a higher order function that consumes "dependencies" and a result function, and returns a function that consumes thestate
object. When you callselectSubtotal
and passstate
you are really passingstate
to a selector tree (or set of trees).For example, when
state.items
updates theselectItems
selector that is an input toselectSubtotal
will update its return value. This update will triggerselectSubtotal
to then recompute its value, and so on for any other selectors that useselectSubtotal
as an input.If you are curious, the resultant selector function will also have a
resultFunc
property that matches the computing function originally passed tocreateSelector
. For example,selectTipAmount.resultFunc
will be(subtotal, tipPercentage) => subtotal * (tipPercentage / 100)
, called likeselectTipAmount.result.Func(subTotal, tipPercentage);
. I use this heavily when unit testing my selectors since I really don't care about memoizing a value, but rather that they correctly compute derived state; I won't need to mock up an entire state object that matches the selector dependencies.