From e977d8ef2437bf47fef9e1d1b04068146ccf839d Mon Sep 17 00:00:00 2001 From: Parkreiner Date: Fri, 15 Sep 2023 20:50:02 +0000 Subject: [PATCH 1/4] fix: Switch UserAutocomplete to useDebouncedFunction --- .../UserAutocomplete/UserAutocomplete.tsx | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index b144c13bc072d..1017741c09033 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -6,10 +6,17 @@ import { useMachine } from "@xstate/react"; import { User } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/AvatarData/AvatarData"; -import debounce from "just-debounce-it"; -import { ChangeEvent, ComponentProps, FC, useEffect, useState } from "react"; +import { + ChangeEvent, + ComponentProps, + FC, + useEffect, + useRef, + useState, +} from "react"; import { searchUserMachine } from "xServices/users/searchUserXService"; import Box from "@mui/material/Box"; +import { useDebouncedFunction } from "hooks/debounce"; export type UserAutocompleteProps = { value: User | null; @@ -31,16 +38,23 @@ export const UserAutocomplete: FC = ({ const [searchState, sendSearch] = useMachine(searchUserMachine); const { searchResults } = searchState.context; - // seed list of options on the first page load if a user pases in a value - // since some organizations have long lists of users, we do not load all options on page load. + // seed list of options on the first page load if a user passes in a value + // since some organizations have long lists of users, we do not load all + // options on page load. + const onMountRef = useRef(value); useEffect(() => { - if (value) { - sendSearch("SEARCH", { query: value.email }); + const mountValue = onMountRef.current; + if (mountValue) { + sendSearch("SEARCH", { query: mountValue.email }); } - // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO look into this - }, []); - const handleFilterChange = debounce( + // 2023-09-15 - This isn't in XState's docs, but its source code guarantees + // that the memory reference of sendSearch will stay stable across renders. + // This useEffect call will behave like an on-mount effect and will not ever + // need to resynchronize + }, [sendSearch]); + + const { debounced: debouncedOnChange } = useDebouncedFunction( (event: ChangeEvent) => { sendSearch("SEARCH", { query: event.target.value }); }, @@ -93,7 +107,7 @@ export const UserAutocomplete: FC = ({ className={styles.textField} InputProps={{ ...params.InputProps, - onChange: handleFilterChange, + onChange: debouncedOnChange, startAdornment: value && ( {value.username} From 6a10a2bc1daec114ac758dae859d71b65fba4420 Mon Sep 17 00:00:00 2001 From: Parkreiner Date: Fri, 15 Sep 2023 20:56:51 +0000 Subject: [PATCH 2/4] fix: switch UserOrGroupAutocomplete to useDebouncedFunction --- .../UserOrGroupAutocomplete/UserOrGroupAutocomplete.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/UserOrGroupAutocomplete/UserOrGroupAutocomplete.tsx b/site/src/components/UserOrGroupAutocomplete/UserOrGroupAutocomplete.tsx index 8d400ecac46a5..51c31a8320c5d 100644 --- a/site/src/components/UserOrGroupAutocomplete/UserOrGroupAutocomplete.tsx +++ b/site/src/components/UserOrGroupAutocomplete/UserOrGroupAutocomplete.tsx @@ -5,11 +5,11 @@ import Autocomplete from "@mui/material/Autocomplete"; import { useMachine } from "@xstate/react"; import { Group, User } from "api/typesGenerated"; import { AvatarData } from "components/AvatarData/AvatarData"; -import debounce from "just-debounce-it"; import { ChangeEvent, useState } from "react"; import { getGroupSubtitle } from "utils/groups"; import { searchUsersAndGroupsMachine } from "xServices/template/searchUsersAndGroupsXService"; import Box from "@mui/material/Box"; +import { useDebouncedFunction } from "hooks/debounce"; export type UserOrGroupAutocompleteValue = User | Group | null; @@ -44,7 +44,7 @@ export const UserOrGroupAutocomplete: React.FC< return !excludeIds.includes(result.id); }); - const handleFilterChange = debounce( + const { debounced: handleFilterChange } = useDebouncedFunction( (event: ChangeEvent) => { sendSearch("SEARCH", { query: event.target.value }); }, From 0e00bd587304823df8e26df0ee2d5badb13aed1f Mon Sep 17 00:00:00 2001 From: Parkreiner Date: Fri, 15 Sep 2023 21:00:30 +0000 Subject: [PATCH 3/4] chore: remove just-debounce-it dependency --- site/package.json | 1 - site/pnpm-lock.yaml | 7 ------- 2 files changed, 8 deletions(-) diff --git a/site/package.json b/site/package.json index 5e14aefdef007..05c3eb52e6f75 100644 --- a/site/package.json +++ b/site/package.json @@ -158,7 +158,6 @@ "jest-runner-eslint": "2.1.0", "jest-websocket-mock": "2.5.0", "jest_workaround": "0.1.14", - "just-debounce-it": "3.2.0", "msw": "1.3.0", "prettier": "3.0.0", "protobufjs": "7.2.4", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index a33950adfc63d..1eae9dbdbb96e 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -386,9 +386,6 @@ devDependencies: jest_workaround: specifier: 0.1.14 version: 0.1.14(@swc/core@1.3.38)(@swc/jest@0.2.24) - just-debounce-it: - specifier: 3.2.0 - version: 3.2.0 msw: specifier: 1.3.0 version: 1.3.0(typescript@5.1.6) @@ -10416,10 +10413,6 @@ packages: object.values: 1.1.6 dev: true - /just-debounce-it@3.2.0: - resolution: {integrity: sha512-WXzwLL0745uNuedrCsCs3rpmfD6DBaf7uuVwaq98/8dafURfgQaBsSpjiPp5+CW6Vjltwy9cOGI6qE71b3T8iQ==} - dev: true - /keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: From 1903e42d43cc9950c4346703e1457dd04edc3159 Mon Sep 17 00:00:00 2001 From: Parkreiner Date: Mon, 18 Sep 2023 13:43:13 +0000 Subject: [PATCH 4/4] docs: Clean up comments --- .../UserAutocomplete/UserAutocomplete.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index 1017741c09033..f76b61e60529e 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -38,9 +38,9 @@ export const UserAutocomplete: FC = ({ const [searchState, sendSearch] = useMachine(searchUserMachine); const { searchResults } = searchState.context; - // seed list of options on the first page load if a user passes in a value - // since some organizations have long lists of users, we do not load all - // options on page load. + // Seed list of options on the first page load if a user passes in a value. + // Since some organizations have long lists of users, we do not want to load + // all options on page load. const onMountRef = useRef(value); useEffect(() => { const mountValue = onMountRef.current; @@ -48,10 +48,10 @@ export const UserAutocomplete: FC = ({ sendSearch("SEARCH", { query: mountValue.email }); } - // 2023-09-15 - This isn't in XState's docs, but its source code guarantees - // that the memory reference of sendSearch will stay stable across renders. - // This useEffect call will behave like an on-mount effect and will not ever - // need to resynchronize + // This isn't in XState's docs, but its source code guarantees that the + // memory reference of sendSearch will stay stable across renders. This + // useEffect call will behave like an on-mount effect and will not ever need + // to resynchronize }, [sendSearch]); const { debounced: debouncedOnChange } = useDebouncedFunction(