-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
QueryControls
: refactor away from lodash.groupBy
#48779
Conversation
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 to me 👍
There's a couple of suggestions to handle before merging this, but I think it looks great and ready to ship afterwards 🚀
'parent' | ||
); | ||
if ( termsByParent.null && termsByParent.null.length ) { | ||
if ( ! ensureParents( flatTermsWithParentAndChildren ) ) { |
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 wonder if we could just inline ensureParents()
. For me it was easier to read this line:
terms.every( ( term ) => term.parent !== null )
than having to read what ensureParents()
does. It's not immediately clear what the function does too - does it add a parent to each term? Or does it filter only terms with parents? I wouldn't expect from a function with this name to return true
only when ALL passed terms have parents. Inlining the condition will solve this mystery IMHO.
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.
Inlining didn't work for me because TypeScript doesn't infer that parent
isn't null
anymore. That's why I created a type guard. That's the important part:
terms is ( TermWithParentAndChildren & { parent: number } )[]
(L13)
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 see. Let's think of a name that matches the purpose better then.
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 have an idea — what if we tweaked the way the termsByParent
dictionary is created, by literally using the 'null'
string as a fallback key when the parent
is actually null
?
Something like this
diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts
index 951ca626be..9f5688fc91 100644
--- a/packages/components/src/query-controls/terms.ts
+++ b/packages/components/src/query-controls/terms.ts
@@ -8,11 +8,6 @@ import type {
TermsByParent,
} from './types';
-const ensureParents = (
- terms: TermWithParentAndChildren[]
-): terms is ( TermWithParentAndChildren & { parent: number } )[] => {
- return terms.every( ( term ) => term.parent !== null );
-};
/**
* Returns terms in a tree form.
*
@@ -29,22 +24,23 @@ export function buildTermsTree( flatTerms: readonly ( Author | Category )[] ) {
id: String( term.id ),
} ) );
- if ( ! ensureParents( flatTermsWithParentAndChildren ) ) {
- return flatTermsWithParentAndChildren;
- }
-
const termsByParent = flatTermsWithParentAndChildren.reduce(
( acc: TermsByParent, term ) => {
const { parent } = term;
- if ( ! acc[ parent ] ) {
- acc[ parent ] = [];
+ const parentKey = parent ?? 'null';
+ if ( ! acc[ parentKey ] ) {
+ acc[ parentKey ] = [];
}
- acc[ parent ].push( term );
+ acc[ parentKey ].push( term );
return acc;
},
{}
);
+ if ( termsByParent.null && termsByParent.null.length ) {
+ return flatTermsWithParentAndChildren;
+ }
+
const fillWithChildren = (
terms: TermWithParentAndChildren[]
): TermWithParentAndChildren[] => {
We could even decide to use another string instead of 'null'
— there shouldn't be any risk of conflict with other dictionary keys, sinceparent
should be a number
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 think that's essentially how it was solved before. I'll have another look.
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 think that's essentially how it was solved before.
Yup, it looks like it. We could keep the same logic, but make it more explicit with a better key name than 'null'
? It could be a quick way to refactor this PR while introducing the least amount of runtime changes possible.
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 do think that's a good use case for .every
(or .some
) and drop out early before constructing the object and then throwing it away (in case). I'll have another look and see what I come up with.
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.
Sounds good. It looks like the buildTermsTree
already has some unit tests, so we should have good confidence in applying the changes you suggested!
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.
In a3745b0 I tried to find a better name for the type guard and added a comment about why we're using it. I think that should address the original concern. Please let me know if there's any other feedback!
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'd name the guard function areAllParentsDefined
or allTermsHaveParents
but then I'd leave it to your judgment.
flatTerms.map( ( term ) => ( { | ||
children: [], | ||
parent: null, | ||
...term, | ||
id: String( term.id ), | ||
} ) ); |
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 shorter version wins 👍
f979041
to
a3745b0
Compare
Flaky tests detected in 9f9e3c8. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4403573166
|
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 from my perspective 👍
cc @ciampo for a final review since he also had some prior feedback.
packages/components/CHANGELOG.md
Outdated
@@ -23,6 +23,7 @@ | |||
- `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). | |||
- `withSpokenMessages`: HOC: Convert to TypeScript ([#48163](https://github.com/WordPress/gutenberg/pull/48163)). | |||
- `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). | |||
- `QueryControls`: Refactor away from Lodash (`.groupBy`) ([#48779](https://github.com/WordPress/gutenberg/pull/48779)) |
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.
Nit:
- `QueryControls`: Refactor away from Lodash (`.groupBy`) ([#48779](https://github.com/WordPress/gutenberg/pull/48779)) | |
- `QueryControls`: Refactor away from Lodash (`.groupBy`) ([#48779](https://github.com/WordPress/gutenberg/pull/48779)). |
'parent' | ||
); | ||
if ( termsByParent.null && termsByParent.null.length ) { | ||
if ( ! ensureParents( flatTermsWithParentAndChildren ) ) { |
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'd name the guard function areAllParentsDefined
or allTermsHaveParents
but then I'd leave it to your judgment.
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.
cc @ciampo for a final review since he also had some prior feedback.
🚀
Thank you for the extra-effort in improving the readability of the code!
9f9e3c8
to
58ea300
Compare
I've just opened a very similar PR in the editor package: #49224. Those should likely come from the same place, but it was like that before, so this is definitely work for another PR. |
What?
This PR removes Lodash's
_.groupBy()
from thebuildTermsTree
function used inQueryControls
Why?
Lodash is known to unnecessarily inflate the bundle size of packages, and in most cases, it can be replaced with native language functionality. See these for more information and rationale:
@wordpress/api-fetch
package haslodash
as a dependency #39495How?
Replaces
_.groupBy
with a native.reduce
function.Testing Instructions
QueryControls
still work as expected