-
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
DataViews: simplify selection setting #62846
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. |
packages/dataviews/src/types.ts
Outdated
import type { ReactElement, ReactNode } from 'react'; | ||
import type { ReactElement, ReactNode, Dispatch, SetStateAction } from 'react'; | ||
|
||
export type setSelection = Dispatch< SetStateAction< string[] > >; |
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.
What does "dispatch" do?
--
The rest of the comment here is not blocking, mostly discussion and typescript learnings / guidelines discussion.
I think there's a balance to be found between the types that make sense as exported or not. For smaller types like this, I think duplication may be fine (avoid indirection). But I don't know if folks more familiar with typescript think. cc @sirreal @jsnajdr
Also curious what @mcsf thinks here.
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 can revert 2e2f38c if you like
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 I see what Dispatch does, it's useful I'd say :)
Curious about the guidelines on exposing this kind of low level types.
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.
Yeah, I forgot I changed a public type too. I can revert it to ( selection: string[] | ( ( prevState: string[] ) => string[] ) ) => void
to unblock if you prefer.
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.
My feedback is that it just took me non-trivial effort to figure out what this Dispatch<SetStateAction>
actually is. Better to write it plainly as @ellatrix proposes. Also make the first letter of the type name uppercase, SetSelection
🙂
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.
Convention is typically to capitalize types, it might be good to change the type name setSelection
to SetSelection
.
This type seems to be used repeatedly and could be error prone to write out over and over, it seems like there's value in a reusable type.
As far as exporting the type or not, I'm not sure how important the questions is. The truth is that any types that are part of the public interface are effectively public types because they will be visible externally.
This type is directly exported, so I guess that may be the question:
export type * from './types'; |
There, I would tend to be cautious. Is this something that is immediately useful for third parties? If not, then we might still export a single type and use it throughout the repository, but not export it directly. Again, ultimately the type will be visible and therefore accessible externally, the question is really about how much of the public API or how visible it should be. It's not the same to access it in laborious ways or just import the type.
It looks like this type is changing, but it may be strictly wider? All values that satisfied the old type also satisfy the new type? In that case I don't think it would qualify as a breaking change.
One thing that might be interesting around types is proposed by @luisherranz in #62400. That PR proposes adding automated tests specifically for types. That could be extremely helpful for determining the types of changes in packages because any change that would make code using a previous versions types invalid would be a breaking change.
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.
Thanks for the feedback. I capitalised it, made the type private, and used a simpler type.
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.
One thing that might be interesting around types is proposed by @luisherranz in #62400.
Thank you for mentioning this, @sirreal.
Indeed, I want to propose a way to test types in Gutenberg. This PR shows how it could be done using tsc
.
[ onSelectionChange ] | ||
); | ||
const onSelect = ( item: Item ) => | ||
onSelectionChange( [ getItemId( item ) ] ); |
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.
Note that useCallback
is not needed here, it's just used in a React event callback down the line.
packages/dataviews/src/dataviews.tsx
Outdated
); | ||
const prevSelection = usePrevious( selection ); | ||
|
||
useEffect( () => { |
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.
Instead of an effect, what do you think of keeping exactly the other changes but instead of having an effect we create a callback with exactly the same signature as setSelection so all the other changes are valid, but that also calls the onSelectionChange inside and not only the setSelection? It would remove the need for the effect.
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.
It's a bit less clean. Previously it was using a callback. If we move this all in a callback, it will pretty much re-render all the time based on these dependencies. I guess I can check if that callback was really needed in the first place.
Why do you not like the effect? Cause it could be triggered from outside selection changes? I think that would be expected?
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.
Why do you not like the effect? Cause it could be triggered from outside selection changes? I think that would be expected?
Yah, the main difference with using an effect is that even if the consumer is the one doing the selection change the consumer will still get a callback on the onSelectionChange that may be unexpected. I'm not sure what is the good practice on these cases.
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.
@jorgefilipecosta I changed it
39b58a3
to
b86375d
Compare
Hi @ellatrix, I think there is a bug, on the page and templates table view I'm not able to select items by clicking on their checkbox. |
} ) | ||
); | ||
} | ||
onSelectionChange( ( state ) => |
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.
Can all these calls that use the updater
be rewritten as?
onSelectionChange(
selection.includes(id)
? selection.filter((itemId) => id !== itemId)
: [...selection, id]
);
I haven't checked them all but it seems that where onSelectionChange
is available, selection
also is. If we can, it makes the updater redundant, and the API would simpler without it.
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 API would simpler without it
I haven't changed the API at all, these are just React setState calls. Updater functions are nice because, if you're in memoized callbacks, you don't depend on the state, though I've removed all of those. It's also surfaced an issue: if you click on the checkbox, we're calling setState twice. This worked before because it would set state twice with the same value, but with the the updater, the second call is undoing the last call.
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 your point, thanks for elaborating 👍
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.
Interesting finding regarding the duplicate setState call, I will have a look into that and see if I can fix it.
@jorgefilipecosta The bug is fixed, the cause was this: #62846 (comment) Imo that should be fixed, we are calling setState twice, which is working in trunk because we don't use an updater function. But I think it's outside the scope of this PR so I changed it back to calling setState with the value. |
7c92c8e
to
3b305c5
Compare
What?
onSelectionChange
function to simply be the React state set function, and set selection via IDs rather than items.Why?
Simplicity.
How?
Testing Instructions
Testing Instructions for Keyboard
Screenshots or screencast