reselect: Does not memoize correctly when filtering by ownProps

Hello,

I found a situation in witch reselect does not memoize correctly when filtering by ownProps.

The scenario is as following: We want a component that computes total amount based on year.

  1. State: [{Year : 2018, Amount: 21}, {Year : 2018, Amount: 21}, {Year : 2017, Amount: 42},]

  2. A selector that an accepts year from ownProps and computes the total amount for that year

function getStateList(state) {
    return state;
}

const getFilteredState = (state, props) =>  getStateList(state).filter(s => s.Year === props.year);

function makeGetTotalAmount() {
    return createSelector(
        [getFilteredState],
        (list) => {
            return list.reduce((acc, curr) => {
                return acc + curr.Amount;
            }, 0)
        }
    );
}

export default makeGetTotalAmount;
  1. Two instances of a TotalAmount.js component, each accepting the year as props
<TotalAmount year={2018} />
<TotalAmount year={2017} />
  1. And a makeMapStateToProps in TotalAmount.js component
const makeMapStateToProps = () => {
    const getTotalAmount = selectors.makeGetTotalAmount();

    const mapStateToProps = (state, props) => {
        return {
            totalAmount: getTotalAmount (state, props),
        }
    };

    return mapStateToProps;
}

My state is normalized and this seems like a common issue, in my projects at least. I think the problem is that filter creates a new array instance every time, but there is no apparent workaround and the documentation is not clear on what to do in these kind of scenarios.

Thank you very much for the time and effort!

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (5 by maintainers)

Most upvoted comments

Yes, the problem is that your getFilteredState() selector is returning a new array instance every time, so Reselect will always run the “output selector” you provided.

The answer is to use Reselect to create getFilteredState as well. You may also need to change it so that instead of directly using props.year, it accepts year as a separate argument, roughly like this:

const selectYear = (state, props) => props.year;

const getFilteredState = createSelector(
    [getStateList, selectYear],
    (stateList, year) => stateList.filter(s => s.Year === year)
);