-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
fix: prevent flashing of search results during input #7592
fix: prevent flashing of search results during input #7592
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.
PR Summary
This PR addresses the issue of search results flashing in the CommandMenu component by implementing debouncing for the search input.
- Added useDebounce hook from 'use-debounce' library in
packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx
- Implemented deferredCommandMenuSearch to replace commandMenuSearch for smoother updates
- Introduced loading states for different record types (people, companies, notes, opportunities)
- Added isLoading check to prevent premature "No results found" display
- These changes aim to improve user experience by reducing frequent updates and eliminating the flashing effect
1 file(s) reviewed, 2 comment(s)
Edit PR Review Bot Settings | Greptile
@@ -139,7 +140,21 @@ export const CommandMenu = () => { | |||
const [commandMenuSearch, setCommandMenuSearch] = useRecoilState( | |||
commandMenuSearchState, | |||
); | |||
// Use useDeferredValue to delay the search results update | |||
// const deferredCommandMenuSearch = useDeferredValue(commandMenuSearch); | |||
const [deferredCommandMenuSearch] = useDebounce(commandMenuSearch, 300); |
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.
style: Consider increasing the debounce delay to 500ms for a better balance between responsiveness and performance
const isNoResults = | ||
!matchingCreateCommand.length && | ||
!matchingNavigateCommand.length && | ||
!people.length && | ||
!companies.length && | ||
!notes.length && | ||
!opportunities.length; |
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.
style: This check for isNoResults might be redundant with the isLoading check. Consider combining them
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.
Thank you a lot for contributing to Twenty!
Introducing debouncing is a great idea. It's an important feature for comboboxes, to me.
I found a edge case and wrote about the solution I would try to fix the problem. These kinds of UI components are complex and have many edge cases. They are always tricky to implement well 😄
// const [previousResults, setPreviousResults] = useState<{ | ||
// people: Person[]; | ||
// companies: Company[]; | ||
// notes: Note[]; | ||
// opportunities: Opportunity[]; | ||
// }>({ | ||
// people: [], | ||
// companies: [], | ||
// notes: [], | ||
// opportunities: [], | ||
// }); |
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 should drop the comments.
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 just saw that you tried to store the previous results. That might be a good start to implement the solution I mentioned.
// Use useDeferredValue to delay the search results update | ||
// const deferredCommandMenuSearch = useDeferredValue(commandMenuSearch); |
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 should drop the comments.
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 never used the useDeferredValue and am unsure it's the best way to solve our problem. However, I'm sure that we should use that hook with debouncing to limit the amount of requests we make.
{isNoResults && !isLoading && ( | ||
<StyledEmpty>No results found</StyledEmpty> | ||
)} |
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 this condition doesn't provide the expect behavior when the previous queries returned no result and the new one returns nothing too.
CleanShot.2024-10-11.at.16.54.45.mp4
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.
To prevent flashes, here is what I would do.
While the user types a query or we fetch the data for this query, we want to keep displaying the old results, the last results the server returned to us. The issue is that the useSearchRecords
function returns an empty array of records when it starts refetching. We should be able to access the old results until we receive a successful response.
We should also consider the error case. If the previous results couldn't be fetched because of an error, we have nothing to display while fetching the new results. We can display a blank screen or a loading indicator. I don't have design opinions. Maybe @Bonapara has.
I implemented a similar feature here: https://xstatebyexample.com/search-as-you-type/. We are not using XState in our project, and I'm not suggesting using it. That's just a comparison. Furthermore, here we are implementing a UI/UX pattern called a "combobox" and we might envision in the future having reusable toolkits to implement that at several places in the app 😄
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.
@Vardhaman619 thanks for the PR, here are my feedbacks:
- let's remove the isSearchEnabled flag to simplify the logic. The new search is fully rolled out, let's use useSearchRecords and that's it
- I'm not sure why you need to store previousPeopleFind and co, why not use directly the result from useSarchRecord hook
I think 1) and 2) should greatly simplify the code
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.
That's way better, thank you!
While playing with it it seems that the results are not cleared if there is no results. For instance when you type something (getting results) and then erase your search. |
Thank you for the feedback! Could you please provide a video or screenshot demonstrating the issue? That would help me understand the behavior better and address it more efficiently. |
Actually, I've found the issue and pushed a small fix, LGTM! |
Awarding Vardhaman619: 150 points 🕹️ Well done! Check out your new contribution on oss.gg/Vardhaman619 |
Log
|
/award 150 |
Awarding Vardhaman619: 150 points 🕹️ Well done! Check out your new contribution on oss.gg/Vardhaman619 |
solves #7511