Please read up on selectors in the officialreselect repository before programming complex calculations using selectors. You should understand the basics of how they work first.
As with regular selectors, Redux-ORM selectors can combine the results of multiple selectors.
So the last argument you pass tocreateSelector can be a custom result function that will receive the result of previous selectors as arguments.
const publisherDescription = createSelector( orm.Publisher.name, orm.Publisher.movies, (publisher, movies) =>`${publisher} has published${movies.length} movies.`);publisherDescription(state,1)// Warner Bros. has published 10000 movies.SelectorSpecIn this example,orm.Publisher.name andorm.Publisher.movies are interpreted as input selectors, although in fact they are so-calledSelectorSpec objects created by Redux-ORM.
Don't forget that both of them will receive the
stateand1as arguments as well.
createSelector will automatically try to turn anything beginning withorm into a selector.
ORM instances become sessionsPassing yourORM instance at any position ofcreateSelector will create a session at the corresponding position in the result function's argument list:
const someSelector = createSelector( [ a, b, orm, d],// a, b and d being selectors (_a, _b, session, _d) => session.User.count());This is important because if you want to access Model classes within the result function, you need to get them using a session (likesession.User above).
You always need to pass at least one selector beginning with
ormtocreateSelector().
QuerySet classWhere does theUser.count() call above come from, you're asking?
There are many functions likecount() defined on theQuerySet class. Most of them are copied over to Model classes when a session is created.
Instances ofQuerySet are automatically created when you access a relationship that potentially returns multiple model instances, likebook.authors. To get the corresponding database objects, you would writebook.authors.toRefArray(). In some cases you need to wrap them in Model instances, e.g. in order to access other related models. To do that, usebook.authors.toModelArray() instead.
Let's apply everything we've learned so far into a single useful computation.
// this is just a helper that gets the average value in an arrayconst avg =arr => arr.reduce((a, b) => a + b,0) / arr.length;const publisherAverageRating = createSelector( orm.Publisher.movies.map(orm.Movie.rating), ratings => ratings && (ratings.length ? avg(ratings) :'no movies'));publisherAverageRating(state,1)// 3.5Different values of the primary key argument need to be handled if you want to support them (#282):
const publisherAverageRating = createSelector( orm.Publisher.movies.map(orm.Movie.rating), (state, idArg) => idArg, (ratingsArg, idArg) => {if (typeof idArg ==='undefined' ||Array.isArray(idArg)) {// only state was passed, or an array of IDsreturn ratingsArg.map(ratings => ratings && ratings.length ? avg(ratings) :'no movies' ); }// single publisher ID was passedreturn ratingsArg && (ratingsArg.length ? avg(ratingsArg) :'no movies'); });Remove the second argument that you previously needed to pass tocreateSelector and pass it to theORM constructor instead (as describedin the previous section).
-const someSelector = createSelector(orm, state => state.orm, …);+const someSelector = createSelector(orm, …);