Hello, I'm sorry if this was answered in the past but I couldn't find anything definitive.
While I understand that we should be using createSelector & createStructuredSelector in our selectors. What about the mapStateToProps function? Should we be using reselect to create that function as well, I've seen people do both. What are the best practices?
const mapStateToProps = createStructuredSelector
({
A: SomeSelector.a(), // === createSelector (...)
B: SomeSelector.b(), // === createSelector (...)
C: SomeSelector.c() // === createSelector (...)
});
versus
const mapStateToProps = state =>
({
A: SomeSelectors.a (state), // === createSelector (...)
B: SomeSelectors.b (state), // === createSelector (...)
C: SomeSelectors.c (state) // === createSelector (...)
});
Those two would be equivalent. Both result in functions that receive state as a parameter, and use more selector functions to extract data and return an object with the data as fields.
Would there be any performance benefits or would you recommend just not using reselect when mapping state to props?
We _highly_ recommend using Reselect in general, since memoized selectors cut down on the number of new object/array references being returned from mapState functions.
For much more info on the topic, see:
mapStateThank you for your response! I'd like to recommend maybe adding this information to the readme file because at the moment I only see it using reselect in the selectors. Instead you may want to add some examples of using reselect for your containers as well. Please close this if there are no further comments!
I think the answer is that you use Reselect to create the memoized selector functions, but those functions could be used anywhere in the application. They'd be most commonly used inside your mapState functions, but you could (and probably should) use them anywhere you're interacting with the Redux state tree. That includes thunks (where you can call getState()), sagas (where you can call yield select(selector), and even in reducers.
AFAIK Redux memoizes mapStateToProps similar to how Reselect memoizes selectors. What's different though is that mapStateToProps gets recomputed when state stays the same but props change (even if your selectors don't make use of it), so technically if your component does not use any non-Redux props (or non-Redux props are guaranteed not to change) using createStructuredSelector has a bit of extra overhead because its memoization becomes redundant.
In the docs for mapStateToProps it says that the function passed to connect for mapStateToProps can optionally accept a 2nd parameter called ownProps and that if defined, it will impact how frequently the function is called... so does the createStructuredSelector return a function by default that accepts both params (state, ownProps) or just state? If it returns a method that includes ownProps, does this have a performance impact?
Finally, when using createStructuredSelector like so:
const mapStateToProps = createStructuredSelector({
foo: selectFoo, // === createSelector(inputSelectors...)
});
are both state and ownProps passed automagically to each input selector? or do they have to be passed manually like so:
const mapStateToProps = (state, props) => {
return createStructuredSelector({
foo: selectFoo(state, props), // === createSelector(inputSelectors...)
});
};
connect does some magic to determine how many arguments the function is declared as taking - specifically, checking mapState.length. If it's 1, it'll be called with (state). If it's 2, it'll be called with (state, ownProps). I don't know how structured selectors get declared, but you can check that yourself.
In general, yes, Reselect passes _every_ argument to _every_ input selector. If I do mySelector(state, "a", 2, {blah: 42}), all four args get passed to all of them.
@markerikson
where does reselect get the arguments it passes to each input selector?
if I do:
const selectA = (...) => ...
const selectB = (...) => ...
const selectFoo = createSelector(
selectA,
selectB,
(a, b) => ...
);
const mapStateToProps = createStructuredSelector({
foo: selectFoo
});
export default connect(mapStateToProps,...)(MyComponent);
what gets passed as params to selectA, selectB and selectFoo, when does it happen and who is responsible for doing it?
What I just said. Every argument _you_ pass to selectFoo will get passed to selectA and selectB, by Reselect, because that's how Reselect works.
@markerikson but I'm not explicitly passing ANY params to any function...
foo: selectFoo // no params being passed here, but yet somehow it still gets state? does it also get ownProps? if so, how and by whom?
I've already explained how this works.
React-Redux's connect function checks to see how many arguments your mapState function is declared as accepting.
Reselect forwards all arguments to all input selectors.
Therefore, _if_ the output of createStructuredSelector is detected as accepting two args, connect will call it as mapState(state, ownProps), and Reselect will forward both arguments to all input selectors.
@markerikson I don't know why I'm having such a hard time explaining myself... maybe I'm missing something..
createStructuredSelector is a helper method that returns a function, right? this function is subsequently assigned to the const mapStateToProps and passed as the first arg to connect, right? So then what is the signature of the the function created by createStructuredSelector(...)? is it:(state) => ...
or
(state, props) => ...
createStructuredSelector somehow analyze every internal selector passed via the given prop: selector map to find out which ones require ownProps and which don't? how does it know if selectA or selectB, which are themselves passed as params to createSelector for selectFoo, require props?again:
const mapStateToProps = createStructuredSelector({
foo: selectFoo, // selectA takes (state, props), selectB takes (state), selectFoo = createSelector(selectA, selectB, (a, b) => {...})... what is mapStateToProps?
});
if I was doing:
mapStateToProps = (state, props) => {
return {
foo: selectFoo(state, props)
}
};
I would clearly see how these are being passed down, but when doing:
mapStateToProps = createStructuredSelector({
foo: selectFoo
});
it is not clear how these are being passed down...
does that make sense?
And tbh, I'm not sure what part of my explanation isn't clear here.
The selector functions generated by createSelector/createStructuredSelector take an _infinite_ number of inputs. They are not declared as (state, ownProps) specifically. They are declared as taking 0 arguments, and then use the JS arguments keyword to grab _however many actual argument values were passed in_, whether it be 0, 1, 25, or one million. See the actual source here: https://github.com/reduxjs/reselect/blob/ac77610bbb0a3cab9b280ea5ea379c2387017446/src/index.js#L52-L87 .
Similarly, those selector functions _do not analyze any input selectors_. They _always pass all arguments on to all input selectors_. If I call selectSomething(1, 2, 3, 4, 5), each input selector will be called as selectSomeInput(1, 2, 3, 4, 5).
In fact, Reselect knows _nothing_ about "props". Despite the name and the tagline, Reselect is not specific to Redux or React-Redux in any way. It is a generic JS library for producing functions that have a single level of memoization. I can call a Reselect selector with selectSomething(state), selectSomething(state, ownProps), selectSomething(state, ownProps.id), or selectSomething("a", "blah", 42, {whatever: true}). It doesn't matter. Reselect doesn't care.
So, for both createSelector and createStructuredSelector, Reselect will look at _all_ of the input selector functions, whether they were passed in via an array for createSelector, or an object for createStructuredSelector, and call _all_ of them with _all inputs_.
Then we turn over to the React-Redux side. As I've said already, React-Redux checks the someFunction.length property, which is a number that matches how many arguments the function was declared as taking. If I have const someFunction = (a, b) => {}, someFunction.length === 2.
connect specifically checks that value to see if a mapState function is declared as taking 1 or 2 args. If it's 1, it gets called with (state). If it's 2, it gets called with (state, ownProps). If it's 0 or any other number, it gets called with (state, ownProps). That check is here:
So. Here's what happens:
connect sees that your createStructuredSelector selector (which is being used as a mapState function) has a length of 0. It calls mapState(state, ownProps)selectFoo(state, ownProps), because those are the entire two arguments that were used.I'd strongly encourage you to actually read the Reselect source. It's not long, and it would answer your questions. There's no magic here - just some functions that take arguments and call other functions:
https://github.com/reduxjs/reselect/blob/ac77610bbb0a3cab9b280ea5ea379c2387017446/src/index.js
@markerikson Ok, thank you... this was the part I was missing:
connect sees that your createStructuredSelector selector (which is being used as a mapState function) has a length of 0. It calls mapState(state, ownProps)
I forgot that when mapStateToProps function has no args, connect passes both state, props by default...
so, then the function returned by createStructuredSelector simply passes the params passed to it by connect to all child / mapped selectors... that makes sense now... thanks!
Most helpful comment
@markerikson Ok, thank you... this was the part I was missing:
I forgot that when
mapStateToPropsfunction has no args,connectpasses both state, props by default...so, then the function returned by
createStructuredSelectorsimply passes the params passed to it byconnectto all child / mapped selectors... that makes sense now... thanks!