react-redux
react-redux copied to clipboard
[Docs]: improve usage of second arg of `useSelector` (equality function)
The documentation talks about using shallowEqual
as second argument for comparison (or lodash), although the documentation doesn't explain what are the arguments of this function and how a user could write their own custom equality function.
Is there a way to improve that part of the documentation?
Yes, make a PR :)
@timdorr Sure, please give me some guideline about what to write inside the documentation
The TS type declarations over in DT are pretty simple:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d459d36f4e1f47c3c6273b287718c79426e41a9f/types/react-redux/index.d.ts#L491
/**
* Compares two arbitrary values for shallow equality. Object values are compared based on their keys, i.e. they must
* have the same keys and for each key the value must be equal according to the `Object.is()` algorithm. Non-object
* values are also compared with the same algorithm as `Object.is()`.
*/
export function shallowEqual(left: any, right: any): boolean;
Easiest thing to do would be to replace the args on useSelector(selector: Function, equalityFn?: Function)
with more specific types than just Function
. Maybe just (a: any, b: any) => Boolean
?
As an aside: In my mind, Function
is pretty close to any
, since you can put basically any function in there. I'm not sure what the internal type representation is there, but it would seem like defining (...args: any[]) => any
, which is super duper loose.
Edit: Yep, what Mark said jibes with that.
And yeah, when I wrote the Hooks docs page in the first place, I was still relatively new to TS. So, I wrote some pseudo-ish type declarations. Close enough to get across the general idea, but not actually based on real types. (That said, I also didn't think it was appropriate to try to spell out every last generic arg in the API docs either, for clarity.)
I saw the types and the source code and I think the types are too generic. From what I understood, left
and right
are basically what's returned by the selector, right?
No. shallowEqual
is a standalone function that can be used separately, and it literally does accept any
arguments. That's kind of the point of the function in the first place: "are these two things basically the same?".
Docs start with const result: any = useSelector(selector: Function, equalityFn?: Function)
which is quite understandable.
And describes that the default is a "strict reference check".
That being said, a paragraph below casually uses shallowEqual
and then casually references lodash
.
If no PR is forthcoming, I'd say, maybe close?
Personally I'd prefer it if redux had ready-made comparison functions for common cases available in a submodule...
I understand that shallowEqual
per se accepts two properties, but when used with useSelector
, what's inside left
and right
?
By doing some logging, I can see left
and right
are the portion of the state selected by the selector.
IMO the returns value of useSelector
, i.e. selected state as that's what determines if the component will be rerendered.
I think it matters when only part of selected state is used by the components, for example:
const {name, email} = useSelector(state => state.xxx.yyy.users[zzz]);
Where user has other fields, e.g. address, but the component ignores them.
Edit: it's already documented so:
When an action is dispatched, useSelector() will do a reference comparison of the previous selector result value and the current result value.
Yeah, it's the previous and current selected value that are passed to the compare function.
In other words, the point is to decide "has the selected value changed at all?" in order to determine if the component actually needs to re-render or not.
It would be great if this equality function would be explained in plain ol' Javascript. Right now I'm stuck with a useless project because of this and it's been a few days now. As soon as I log in, dashboard renders without the data that was retrieved and stored in store, and I can see the error in the username (no username). As soon as I refresh the page, the username appears.
Please let me know if this is going to be addressed. Otherwise I'll just stick with react useContext.
@alvamanu : The type signature is:
export type EqualityFn = (left: any, right: any) => boolean;
(loosely - I haven't added that specific type to the typedefs)
In other words:
- it takes any two arguments
- and returns a boolean indicating if they are equal or not
Per our source, the default equality check is a standard reference equality comparison:
const refEquality = (a, b) => a === b
Any other function that takes two values and returns a boolean is acceptable here.
I'll note that we did ask for someone from the community to file a docs PR a year and a half ago, and no one has done so yet :)
If you're having usage issues, your best bet is to ask on Stack Overflow or Reactiflux - we're happy to try to answer questions, but this issue isn't a good place to ask for help.
Thank you @markerikson. I see you don't really have to use shallowEqual. useSelector takes two arguments, the second one being a function that takes two arguments:
the first argument being the past state of what's being selected, and the second one being the new state of what's being selected.
A simple (oldState, newState) => oldState === newState
will suffice.
I tested this with two buttons, each dispatching a nameChange action with a different argument. Works great onclick. My issue is that even though store is updated on logging, new and old state are blank after the login redirect. I can see the store is updated in redux-logger, just not in my component. I want for my component to grab the username from the newly updated store and place it on component load.
That being said, I know now that's not the issue that I'm having so at least it's a step forward for me. Thanks again!
Yeah, and that is the default behavior of useSelector
already - it does that exact ===
comparison if you don't provide an equalityFn
yourself
I'm new to react-redux
and was surprised to not find API reference for shallowEqual
. IMO, the docs aren't complete without API references for each part of the library API.
I appreciate that this is an open-source project, and it's a lot easier to ask for something than it is to provide it :)
Perhaps after I've been using the library a bit longer I can take a stab at a PR.
@dwaltrip : tbh it's a trivial enough function that there's never been a specific need to document it :) (Plus in this case you don't normally call it directly, you just import it and pass it as the second arg to useSelector
.)
But yeah, happy to have a PR!