-
-
Notifications
You must be signed in to change notification settings - Fork 266
[composite] Fix CompositeList not updating item order on reordering #2675
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
Conversation
commit: |
✅ Deploy Preview for base-ui ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
59d131c to
4696313
Compare
3d2854a to
5e1d3ef
Compare
| act(() => item1.focus()); | ||
| await user.keyboard('{ArrowDown}'); | ||
| expect(item3).toHaveFocus(); | ||
| }); |
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.
This fails on the main branch when running the test after doing node scripts/useReactVersion.mjs ^18
5e1d3ef to
52401e3
Compare
|
|
||
| const map = useRefWithInit(createMap<Metadata>).current; | ||
| const [mapTick, setMapTick] = React.useState(0); | ||
| const [mapTick, setMapTick] = React.useState({}); |
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.
Update to use just empty object instead because there is a (very low) risk of number overflow.
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.
There's a static empty obj that could be used: https://github.com/mui/base-ui/blob/master/packages/react/src/utils/constants.ts#L4
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.
This should be reverted, there is no realistic risk of number overflow. Number.MAX_SAFE_INTEGER is the upper limit for integers. Even with 1,000 updates per second, it would take 285 years before it causes problems.
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!
You're right that the risk of number overflow is practically negligible. I only meant to highlight that "negligible" isn't quite the same as "impossible", but my main motivation here was consistency with other parts of the codebase that use the object pattern, such as the one used in useForceRerendering.
That said, I don't feel strongly about it, and I'm happy to revert this change if the project prefers to keep the counter-based approach here when I have a chance.
Would you prefer I update this line back, or align the other occurrences instead? Thanks!
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 would switch this line back to a number. And tbh number overflow is not possible with JS numbers, the issue would be hitting the double precision limit which would render the + 1 operation void. Number overflow would actually not be an issue, it would be a good thing. We could wrap on the int32 range by casting via >>> 0 to get number overflow.
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.
Sorry, I was not very clear above. I'm aware of that behavior and understand that +1 can become ineffective (void) in JS. My point was that, this could cause Object.is in React to return true, preventing re-renders and memo updates.
That said, as you mentioned, this risk is extremely low, and I'm cool with reverting this change. When we do that, I'd like to leave a comment to prevent the same change from happening again since this change has passed the review, and I'll also need to make sure to mention it in the PR. To clarify, when explaining why the counter approach is used, is it sufficient to mention it for memory efficiency (GC)? Thanks again!
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.
We can use setMapTick(t => (t + 1) >>> 0) to wrap around if we're really worried about hitting the precision limit, and yes we can mention memory efficiency.
| if (labelsRef && labelsRef.current.length !== sortedMap.size) { | ||
| labelsRef.current.length = sortedMap.size; | ||
| } | ||
| nextIndexRef.current = sortedMap.size; |
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.
Found nextIndexRef only keeps incrementing.
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.
@chuganzy I found this line causes a bug in Autocomplete + autoHighlight where the scroll on the page jumps on the second+ open.
Did this actually cause a bug or can it be removed?
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.
@atomiks My bad.. Yes, this line can be isolated and reverted! 🙏
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.
Nevermind, it just uncovered a bug actually. #2815
Bundle size report
|
|
Tested the fix and it works: https://codesandbox.io/p/devbox/2675-forked-68z63n |
Curious if this only happens in React 18 dev mode? See: #2565 We also want to make sure performance doesn't degrade for composite list components if possible: |
51257dd to
e6806da
Compare
|
Quick summary of performance check:
Given the above, in my opinion these results are acceptable, but what do you think? |
atomiks
left a comment
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 contribution! Performance seems unaffected, and the fix works correctly
This PR fixes a bug in CompositeList where the internal item order was not updated when the list items were reordered
in React versions earlier than 19(this was also the case in React 19 production build) and as a result, arrow key navigation could behave incorrectly.After doing some tests, it turned out React 19 remounts items accordingly and this is not an issue.
Sample project to reproduce that issue: https://codesandbox.io/p/sandbox/qfsgqw
This PR fixes the issue by making use of MutationObserver
if the React version is <19.