Reselect Utils
Edit page
IntroductionQuick Start
Guides
Path & Prop SelectorsPath SelectorsProp SelectorBound & Adapted SelectorsStructured & Sequence SelectorsChain & Empty SelectorsComposing Key Selector Creator
Graphs
API

Path & Prop Selectors

Path Selectors

Path Selector helper is useful when you try to describe deep nested dependencies of your selector. Suppose you have such state:

const state = {
person: {
id: 1,
firstName: 'Marry',
secondName: 'Poppins',
},
};

Lets's write a simple selector for it:

const personFullNameSelector = createSelector(
(state) => state.person.firstName,
(state) => state.person.secondName,
(firstName, secondName) => `${firstName} ${secondName}`,
);

It is working example and now we don't need any helper to implement it. But what if our person object is optional. We can re-write our example next way:

const personFullNameSelector = createSelector(
(state) => state.person && state.person.firstName,
(state) => state.person && state.person.secondName,
(firstName, secondName) => `${firstName} ${secondName}`,
);

I can also use optional chaining to reduce and simplify code:

const personFullNameSelector = createSelector(
(state) => state.person?.firstName,
(state) => state.person?.secondName,
(firstName, secondName) => `${firstName} ${secondName}`,
);

Yes, we still don't need any helper now. But what if we have normalized state with few optional persons:

const state = {
persons: {
1: {
id: 1,
firstName: 'Marry',
secondName: 'Poppins',
},
2: {
id: 2,
firstName: 'Harry',
secondName: 'Potter',
},
},
};

In this case you need provide personId property in your selector. Re-reselect library is better for parametric selector, so now we can write something like this:

import { createCachedSelector } from 're-reselect';
const personFullNameSelector = createCachedSelector(
(state, props) => state.persons[props.personId]?.firstName,
(state, props) => state.persons[props.personId]?.secondName,
(firstName, secondName) => `${firstName} ${secondName}`,
)({
keySelector: (state, props) => props.personId,
});

If you use TypeScript for static typing this example can look more complex:

const personFullNameSelector = createCachedSelector(
(state: State, props: Props) => state.persons[props.personId]?.firstName,
(state: State, props: Props) => state.persons[props.personId]?.secondName,
(firstName, secondName) => `${firstName} ${secondName}`,
)({
keySelector: (state, props) => props.personId,
});

Now we can use Path Selector to reduce boilerplate code:

import { createPathSelector } from 'reselect-utils';
const personsSelector = (state: State) => state.persons;
const personSelector = createCachedSelector(
personsSelector,
(state: State, props: Props) => props.personId,
(persons, personId) => persons[personId],
)({
keySelector: (state, props) => props.personId,
});
const personFullNameSelector = createCachedSelector(
createPathSelector(personSelector).firstName(),
createPathSelector(personSelector).secondName(),
(firstName, secondName) => `${firstName} ${secondName}`,
)({
keySelector: (state, props) => props.personId,
});

Prop Selector

There is another Prop Selector helper for properties selection. Prop Selector is built on top Path Selector. Also, there are short aliases for Prop Selector and Path Selector helper. With these helpers you can re-write your code like this:

import { path, prop } from 'reselect-utils';
const personsSelector = (state: State) => state.persons;
const personSelector = createCachedSelector(
personsSelector,
prop<Props>().personId(),
(persons, personId) => persons[personId],
)({
keySelector: prop<Props>().personId(),
});
const personFullNameSelector = createCachedSelector(
path(personSelector).firstName(),
path(personSelector).secondName(),
(firstName, secondName) => `${firstName} ${secondName}`,
)({
keySelector: prop<Props>().personId(),
});

You can set up default values in Path Selector, if you need:

const personFullNameSelector = createCachedSelector(
path(personSelector).firstName('John'), // <- Default value is used here
path(personSelector).secondName('Doe'), // <- and here
(firstName, secondName) => `${firstName} ${secondName}`,
)({
keySelector: prop<Props>().personId(),
});

Now we have fully typed selector, that is cached by different properties.

You can also work with objects of unlimited nesting:

import { path } from 'reselect-utils';
const personSelectorInfo = createSelector(
path(personSelector).address.street('-'),
path(personSelector).some.very.deep.field(123),
(street, field) => ({ street, field }),
)({
keySelector: prop<Props>().personProps.personId(),
});