@@ -6,10 +6,17 @@ import { useMachine } from "@xstate/react";
6
6
import { User } from "api/typesGenerated" ;
7
7
import { Avatar } from "components/Avatar/Avatar" ;
8
8
import { AvatarData } from "components/AvatarData/AvatarData" ;
9
- import debounce from "just-debounce-it" ;
10
- import { ChangeEvent , ComponentProps , FC , useEffect , useState } from "react" ;
9
+ import {
10
+ ChangeEvent ,
11
+ ComponentProps ,
12
+ FC ,
13
+ useEffect ,
14
+ useRef ,
15
+ useState ,
16
+ } from "react" ;
11
17
import { searchUserMachine } from "xServices/users/searchUserXService" ;
12
18
import Box from "@mui/material/Box" ;
19
+ import { useDebouncedFunction } from "hooks/debounce" ;
13
20
14
21
export type UserAutocompleteProps = {
15
22
value : User | null ;
@@ -31,16 +38,23 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
31
38
const [ searchState , sendSearch ] = useMachine ( searchUserMachine ) ;
32
39
const { searchResults } = searchState . context ;
33
40
34
- // seed list of options on the first page load if a user pases in a value
35
- // since some organizations have long lists of users, we do not load all options on page load.
41
+ // Seed list of options on the first page load if a user passes in a value.
42
+ // Since some organizations have long lists of users, we do not want to load
43
+ // all options on page load.
44
+ const onMountRef = useRef ( value ) ;
36
45
useEffect ( ( ) => {
37
- if ( value ) {
38
- sendSearch ( "SEARCH" , { query : value . email } ) ;
46
+ const mountValue = onMountRef . current ;
47
+ if ( mountValue ) {
48
+ sendSearch ( "SEARCH" , { query : mountValue . email } ) ;
39
49
}
40
- // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO look into this
41
- } , [ ] ) ;
42
50
43
- const handleFilterChange = debounce (
51
+ // This isn't in XState's docs, but its source code guarantees that the
52
+ // memory reference of sendSearch will stay stable across renders. This
53
+ // useEffect call will behave like an on-mount effect and will not ever need
54
+ // to resynchronize
55
+ } , [ sendSearch ] ) ;
56
+
57
+ const { debounced : debouncedOnChange } = useDebouncedFunction (
44
58
( event : ChangeEvent < HTMLInputElement > ) => {
45
59
sendSearch ( "SEARCH" , { query : event . target . value } ) ;
46
60
} ,
@@ -93,7 +107,7 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
93
107
className = { styles . textField }
94
108
InputProps = { {
95
109
...params . InputProps ,
96
- onChange : handleFilterChange ,
110
+ onChange : debouncedOnChange ,
97
111
startAdornment : value && (
98
112
< Avatar size = "sm" src = { value . avatar_url } >
99
113
{ value . username }
0 commit comments