Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs]: improve usage of second arg of useSelector (equality function) #1504

Closed
ematipico opened this issue Jan 20, 2020 · 17 comments
Closed
Labels

Comments

@ematipico
Copy link

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?

@timdorr
Copy link
Member

timdorr commented Jan 20, 2020

Yes, make a PR :)

@ematipico
Copy link
Author

@timdorr Sure, please give me some guideline about what to write inside the documentation

@markerikson
Copy link
Contributor

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;

@timdorr
Copy link
Member

timdorr commented Jan 20, 2020

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.

@markerikson
Copy link
Contributor

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.)

@ematipico
Copy link
Author

ematipico commented Jan 20, 2020

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?

@markerikson
Copy link
Contributor

markerikson commented Jan 20, 2020

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?".

@timdorr timdorr added the docs label Jan 31, 2020
@dimaqq
Copy link

dimaqq commented Jul 13, 2020

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...

@ematipico
Copy link
Author

ematipico commented Jul 13, 2020

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.

@dimaqq
Copy link

dimaqq commented Jul 13, 2020

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.

@markerikson
Copy link
Contributor

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.

@alvamanu
Copy link

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.

@markerikson
Copy link
Contributor

markerikson commented Jun 30, 2021

@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.

@alvamanu
Copy link

alvamanu commented Jun 30, 2021

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!

@markerikson
Copy link
Contributor

Yeah, and that is the default behavior of useSelector already - it does that exact === comparison if you don't provide an equalityFn yourself

@dwaltrip
Copy link

dwaltrip commented Nov 28, 2022

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.

@markerikson
Copy link
Contributor

@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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants