I've got a question about selector memoization with Reselect.
As far as I understand the documentation of Reselect, the following implementation is the proposed and correct way to memoize a selector that expects a parameter:
const selectOrdersByCustomer = createSelector(
[
state => state.orders,
(state, customerId) => customerId,
],
(orders, customerId) => {
return orders.filter(order => order.customerId === customerId);
}
);
const orders = useSelector(state => selectOrdersByCustomer(state, customerId));
One of my colleagues came up with this approach of the same selector:
const selectOrdersByCustomer = customerId => createSelector(
state => state.orders,
orders => {
return orders.filter(order => order.customerId === customerId);
}
);
const orders = useSelector(selectOrdersByCustomer(customerId));
(I simplified this, the actual implementation in our application is a fair bit more complicated)
I tried to add console.count('counter');
to the component where this selector is used and it seems like both implementations trigger the same amount of rerenders.
My question: Is there a performance penalty of the second implementation in comparison with the first one?
The second way has a performance issue. Each selector has its own cached result, but a new memoized selector function is created every time the component renders. It will run the output selector multiple times. The cached result will not be used.
import { createSelector } from 'reselect';
const selectOrdersByCustomer1 = createSelector(
[(state) => state.orders, (state, customerId) => customerId],
(orders, customerId) => {
console.count('selectOrdersByCustomer1 output selector');
return orders.filter((order) => order.customerId === customerId);
},
);
const selectOrdersByCustomer2 = (customerId) =>
createSelector(
(state) => state.orders,
(orders) => {
console.count('selectOrdersByCustomer2 output selector');
// @ts-ignore
return orders.filter((order) => order.customerId === customerId);
},
);
const state = { orders: [{ customId: 1 }, { customId: 2 }] };
const x1 = selectOrdersByCustomer1(state, 1);
const x2 = selectOrdersByCustomer1(state, 1);
const x3 = selectOrdersByCustomer1(state, 1);
console.log(selectOrdersByCustomer1.recomputations());
console.log(x1 === x2, x1 === x3);
const s1 = selectOrdersByCustomer2(1)(state);
const s2 = selectOrdersByCustomer2(1)(state);
const s3 = selectOrdersByCustomer2(1)(state);
console.log(s1 === s2, s1 === s3);
Logs:
selectOrdersByCustomer1 output selector: 1
1
true true
selectOrdersByCustomer2 output selector: 1
selectOrdersByCustomer2 output selector: 2
selectOrdersByCustomer2 output selector: 3
false false