From 11fae604be83d11c1ad1874098f9346429365a32 Mon Sep 17 00:00:00 2001 From: Matchu Date: Thu, 21 Jan 2021 16:03:14 -0800 Subject: [PATCH] Fix flash of content when emptying search field 1. Search for something 2. Clear the search bar 3. Quickly start typing something new Before this change, the results would clear on #2, but then the old results would show up again during #3, before the loading state for the new query. This matches the logic, right? We hid the results when both the current and debounced query were empty, and, during that time, neither is empty. Instead, here we update the `useDebounce` hook to have a `forceReset` option, to immediately clear out the value instead of waiting. --- src/app/ItemSearchPage.js | 15 ++++++--------- src/app/util.js | 9 ++++++++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/app/ItemSearchPage.js b/src/app/ItemSearchPage.js index 8c0c553..e4a9e96 100644 --- a/src/app/ItemSearchPage.js +++ b/src/app/ItemSearchPage.js @@ -91,14 +91,11 @@ function ItemSearchPageResults({ query: latestQuery }) { const query = useDebounce(latestQuery, 300, { waitForFirstPause: true, initialValue: emptySearchQuery, - }); - // We'll skip all this if the query is empty. We also check the latest query - // for this, without waiting for the debounce, in order to get fast feedback - // when clearing the query. But we _do_ still check the debounced query too, - // which gives us _slow_ feedback when moving from empty to _non_-empty. - const skipSearchResults = - searchQueryIsEmpty(query) || searchQueryIsEmpty(latestQuery); + // When the query is empty, clear the debounced query immediately too! That + // will give us fast feedback when the search field clears. + forceReset: searchQueryIsEmpty(latestQuery), + }); // NOTE: This query should always load ~instantly, from the client cache. const { data: zoneData } = useQuery(gql` @@ -147,11 +144,11 @@ function ItemSearchPageResults({ query: latestQuery }) { zoneIds: filterToZoneIds, }, context: { sendAuth: true }, - skip: skipSearchResults, + skip: searchQueryIsEmpty(query), } ); - if (skipSearchResults) { + if (searchQueryIsEmpty(query)) { return null; } diff --git a/src/app/util.js b/src/app/util.js index f3aaf63..5a65e09 100644 --- a/src/app/util.js +++ b/src/app/util.js @@ -141,7 +141,7 @@ export function safeImageUrl(urlString) { export function useDebounce( value, delay, - { waitForFirstPause = false, initialValue = null } = {} + { waitForFirstPause = false, initialValue = null, forceReset = false } = {} ) { // State and setters for debounced value const [debouncedValue, setDebouncedValue] = React.useState( @@ -165,6 +165,13 @@ export function useDebounce( [value, delay] // Only re-call effect if value or delay changes ); + // The `forceReset` option sets the value immediately! + React.useEffect(() => { + if (forceReset) { + setDebouncedValue(value); + } + }, [value, forceReset]); + return debouncedValue; }