-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Data: Include more details when shallow equality fails in 'useSelect' #67713
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Size Change: 0 B Total Size: 1.83 MB ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, I often add similar code locally when debugging something. Happy to see it added permanently.
keys = Object.keys( a ).filter( ( k ) => a[ k ] !== b[ k ] ); | ||
} else if ( Array.isArray( a ) && Array.isArray( b ) ) { | ||
keys = [ ...a.keys() ].filter( ( i ) => a[ i ] !== b[ i ] ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Object.keys
should work equally well also for arrays. The only difference is that .keys()
returns numbers, while Object.keys
are strings.
The a.constructor
check will crash when a
is null
or undefined
, we should guard for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Object.keys should work equally well also for arrays. The only difference is that .keys() returns numbers, while Object.keys are strings.
Yeah. I should have guessed that 😅
The a.constructor check will crash when a is null or undefined, we should guard for that.
I've borrowed the check for isShallowEqual
, so it should be safe in this case.
Based on your suggestion, I think we can simplify logic to the example below. What do you think?
const keys =
typeof a === 'object' && typeof b === 'object'
? Object.keys( a ).filter( ( k ) => a[ k ] !== b[ k ] )
: [];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've borrowed the check for
isShallowEqual
, so it should be safe in this case.
But isShallowEqual
does an if ( a && b )
check before it starts looking at a.constructor
.
typeof a === 'object' && typeof b === 'object'
typeof null
is also 'object'
🙀 It will all work only if you exit early when a
or b
is falsy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should stop ignoring the "everything is an object" JS motto 😄
Pushed update in 39bc8c1.
'This can lead to unnecessary re-renders and performance issues if not fixed.\n\n' + | ||
'Non-equal value keys: %s\n\n', | ||
keys.join( ', ' ) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning will be confusing when the b
object has some field that the a
object doesn't have. Then it will report an empty list of non-equal keys.
It can happen in practice when we write code like this:
useSelect( select => {
if ( someCondition ) {
return { loading: false };
}
return { loading: false, data: select( store ).getData() };
} );
To fix this, we could construct a set of all fields on both objects and then filter it:
const allKeys = Array.from( new Set( [ ...Object.keys( a ), ...Object.keys( b ) ] ) );
const keys = allKeys.filter( ( k ) => a[ k ] !== b[ k ] );
But this will fail again for objects like {}
and { a: undefined }
which are not shallow equal but keys
will be []
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning will be confusing when the b object has some field that the a object doesn't have. Then it will report an empty list of non-equal keys.
It means that the b
run happened with different state or parameters (mostly params, based on your example). That shouldn't be the case for this warning, as it only makes sense with the same state/params.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's true, I've been overthinking it 🙂 Running the mapSelect
callback twice with the same state shouldn't produce objects like this unless you're doing it on purpose.
Flaky tests detected in 39bc8c1. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/12232091964
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, I don't have any further JavaScript quirks to point out 🙂
@jsnajdr, as always, thank you for the thoughtful review. |
This is really useful, thank you @Mamaduka 🙌 Also, thank you both for the discussion on handling language subtleties - it is always useful and a pleasure to read! |
I'm considering exposing this warning to third-party consumers when the cc @gziolo |
Discussed also on WordPress Slack here. It sounds like a great idea to me. For me, |
What?
This is a follow-up to #53666.
Closes #63608.
PR updates the warning that's logged when
useSelect
returns different values when run with the same state and parameters. The warning now also includes the keys that fail the check.Why?
A quality of life improvement when debugging
useSelect
issues.Testing Instructions
Test plugin
Testing Instructions for Keyboard
Same.
Screenshots or screencast