-
-
Notifications
You must be signed in to change notification settings - Fork 32.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
[docs][material-ui] Fix Material Icon search lag and other improvements #41330
[docs][material-ui] Fix Material Icon search lag and other improvements #41330
Conversation
Netlify deploy previewhttps://deploy-preview-41330--material-ui.netlify.app/ Bundle size report |
Hey @anle9650, thanks for working on this. I see a problem with this implementation though: Screen.Recording.2024-03-06.at.10.56.22.movIt's missing keystrokes because the query value is not updated immediately. For example if I press I would imagine the solution to be to modify Let me know if you can think of alternative solutions having this in mind. |
@DiegoAndai good catch! That makes sense. I'll try to come up with a solution. |
This reverts commit 2ea4a85.
I think I figured it out. Lmk what you guys think! |
This makes sense to me. The query param seems only to be read on the initial render, so I don't think we'll have synchronization issues between the state and the query param. @Janpot, I noticed you initially implemented the feature. What do you think of this change? I want to ensure I'm not missing context on this feature's history. |
I may be wrong, but I don't believe it's I believe the right approach is to defer the derived state that is used to render the icons, this will allow the UI to rerender in the background and be interruptible when the input value updates: diff --git a/docs/data/material/components/material-icons/SearchIcons.js b/docs/data/material/components/material-icons/SearchIcons.js
index fc2d1f34e1..558606fca7 100644
--- a/docs/data/material/components/material-icons/SearchIcons.js
+++ b/docs/data/material/components/material-icons/SearchIcons.js
@@ -532,6 +532,8 @@ export default function SearchIcons() {
[theme, keys],
);
+ const deferredIcons = React.useDeferredValue(icons);
+
const dialogSelectedIcon = useLatest(
selectedIcon ? allIconsMap[selectedIcon] : null,
);
@@ -579,9 +581,9 @@ export default function SearchIcons() {
/>
</Paper>
<Typography sx={{ mb: 1 }}>{`${formatNumber(
- icons.length,
+ deferredIcons.length,
)} matching results`}</Typography>
- <Icons icons={icons} handleOpenClick={handleOpenClick} />
+ <Icons icons={deferredIcons} handleOpenClick={handleOpenClick} />
</Grid>
<DialogDetails
open={!!selectedIcon} For me this seems to make the input a lot more responsive while the UI is rendering. However, this doesn't solve the lag between typing and rendering the filtered icons. If we want to solve this, I believe virtualization of the icons list is in order, to reduce the amount of elements that are being rendered. In hindsight, the whole thing can probably be simplified after this change. As the render becomes interruptible, we can likely remove the debounce of the search function altogether. Updated this PR with the suggestions and a few other tweaks:
Preview: https://deploy-preview-41330--material-ui.netlify.app/material-ui/material-icons/ Screen.Recording.2024-03-14.at.16.51.10.movTo be clear: this solution solves the search input responsiveness. The INP as reported in #41126 may also be caused by long hydration. I believe this can not be solved without reducing the amount of rendered icons. cc @oliviertassinari |
This PR → #41415 seems to be also touching on some of these aspects regarding the number of displayed icons, input responsiveness, etc. Just want to make sure we don't discuss the same thing in two places 😃 Maybe this one should be prioritized as it's a "narrower" scope than the other?! |
Thanks for taking a deep look into this @Janpot 🙌🏼 your changes make sense to me It seems that there's room to improve the solution further by optimizing the icon's rendering, for example, by reducing or limiting the amount, and also maybe adding pagination. This was also touched upon in #41415. Also, If the goal is to improve INP, is there a way for us to measure if a change is improving it? We should figure that out before continuing to work on the solution. With that, we can test if reducing the amount of icons rendered improves the score. Besides that, I think we should point this PR to the |
I suppose we could use the react profiler. e.g. comparing typing "foo" in the empty box:
|
I’ve virtualized the icons using PR-41330.mp4I’ve also changed the scrolling to be within the Search Icons container, which I think looks better than scrolling the entire page. Additionally, in commits ba2e45c and e083e60, I fixed an intermittent bug where the page becomes unclickable after closing the Icon Dialog. The issue is linked to
This issue isn’t directly related to this PR. I can revert the change and address it separately if needed. URLs to test:
Please also test all changes on a mobile device. |
Running lighthouse on these show barely any improvement. We should further profile the page load. I expect the inclusion of edit:This shouldn't be part of this PR. Just some thoughts I had when I was exploring the performance of this page. We can keep the scope of this PR to what it already is. |
Yes, I also noticed this. It's a good idea. This is especially important because, according to google analytics, this is the most visited page consistently. As for the search performance improvements in this PR, it's ready for review. |
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 ok to me. I can't reproduce the problem with the dialog though.
I won’t cherry-pick this to |
@DiegoAndai We can use https://chromewebstore.google.com/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma?hl=en. I got introduced to it at https://www.youtube.com/watch?v=KZ1kxzsJZ5g&t=223s. After 28 days, we can also use the field data from https://pagespeed.web.dev/ (aggregated data by Chrome sent anonymously). Before: https://deploy-preview-41329--material-ui.netlify.app/material-ui/material-icons/?query=ale INP 1,000ms without much struggle to trigger Screen.Recording.2024-09-01.at.23.20.04.movAfter: https://deploy-preview-41330--material-ui.netlify.app/material-ui/material-icons/ Screen.Recording.2024-09-01.at.23.21.35.movThe INP problems seem solved. Pretty cool! A related issue on this: mui/mui-x#9657. Now, the problems:
"burger" / "facebook", people searching for those icons: https://dashboard.algolia.com/apps/TZGZ85B9TB/analytics/no-results/material-ui?from=2024-08-01&to=2024-08-31 We might want to restore this feature. It could be simply about rendering a hidden list of
To fix this.
diff --git a/docs/data/material/components/material-icons/SearchIcons.js b/docs/data/material/components/material-icons/SearchIcons.js
index d8378f881f..00f470e74e 100644
--- a/docs/data/material/components/material-icons/SearchIcons.js
+++ b/docs/data/material/components/material-icons/SearchIcons.js
@@ -130,6 +130,7 @@ const StyledSvgIcon = styled(SvgIcon)(({ theme }) => ({
}));
const ListWrapper = React.forwardRef(({ style, children, ...props }, ref) => {
+ console.log('ListWrapper');
return (
<div
ref={ref}
@@ -141,6 +142,9 @@ const ListWrapper = React.forwardRef(({ style, children, ...props }, ref) => {
);
});
+const virtuosoComponents = { List: ListWrapper };
+const virtuosoStyle = { height: 500 };
+
function Icon(handleOpenClick) {
return function itemContent(_, icon) {
const handleIconClick = () => {
@@ -528,6 +532,9 @@ export default function SearchIcons() {
const deferredQuery = React.useDeferredValue(query);
const deferredTheme = React.useDeferredValue(theme);
+ console.log('deferredQuery', deferredQuery, query)
+
+ const itemContent = React.useMemo(() => Icon(handleOpenClick), [handleOpenClick]);
const isPending = query !== deferredQuery || theme !== deferredTheme;
const icons = React.useMemo(() => {
@@ -604,10 +611,10 @@ export default function SearchIcons() {
icons.length,
)} matching results`}</Typography>
<VirtuosoGrid
- style={{ height: 500 }}
+ style={virtuosoStyle}
data={icons}
- components={{ List: ListWrapper }}
- itemContent={Icon(handleOpenClick)}
+ components={virtuosoComponents}
+ itemContent={itemContent}
/>
</Grid>
{/* Temporary fix for Dialog not closing sometimes and Backdrop stuck at opacity 0 (see issue https://github.com/mui/material-ui/issues/32286). One disadvanta
ge is that the closing animation is not applied. */} It's documented in https://react.dev/reference/react/useDeferredValue#deferring-re-rendering-for-a-part-of-the-ui. So we solved INP with react-virtuoso, not by using suspense features. How about we revert this and instead only go with deferred value? We can fix the slow render another time but still get a search input that responds quickly to events. We are nowhere near having a performance issue on https://v4.mui.com/components/material-icons/, even if we x2 the time because there are x2 more icons now. So to me, the red flag is Material UI v5, we have to fix this regression we introduced 3 years ago, we sacrificed it for dynamic props, and now CSS variables mean we don't need this anymore. @romgrk has been looking a bit at it lately. |
Agreed, approving this was poor judgement on my side. Reverting back to the changes proposed in #41330 (comment) |
@Janpot Thanks for handling it. I didn't realize it would affect global search, even on Google.
I think you're aiming for something like this: https://fonts.google.com/icons. However, we need the double scrollbar due to other content on the page, like the API section. IMO, It's better than the old version - https://v5.mui.com/material-ui/material-icons/, where you had to scroll all the way down to reach the API section. Maybe rendering a large container would be better. I'll leave it to you guys to decide. |
Did a quick experiment, I think there could be a few low hanging fruits here:
These improved Edit: Looks like building the searchindex takes about 170ms synchronously while the module initializes. We can think about deferring this as well to improve initial load time |
Fixes #41126
Screen.Recording.2024-03-01.at.8.32.19.PM.mov
Preview: https://deploy-preview-41330--material-ui.netlify.app/material-ui/material-icons/