-
Notifications
You must be signed in to change notification settings - Fork 961
fix(site): speed up state syncs and validate input for debounce hook logic #18877
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
Changes from 1 commit
9f35d9b
01850ca
264069e
9cd1c57
af376a2
9ca576b
11217cb
4266b85
b9b5c9f
c25cc8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,28 +35,28 @@ export function useDebouncedFunction< | |
Args extends unknown[] = unknown[], | ||
>( | ||
callback: (...args: Args) => void | Promise<void>, | ||
debounceTimeMs: number, | ||
debounceTimeoutMs: number, | ||
): UseDebouncedFunctionReturn<Args> { | ||
if (!Number.isInteger(debounceTimeMs) || debounceTimeMs < 0) { | ||
throw new Error( | ||
`Provided debounce value ${debounceTimeMs} is not a non-negative integer`, | ||
); | ||
} | ||
if (!Number.isInteger(debounceTimeoutMs) || debounceTimeoutMs < 0) { | ||
throw new Error( | ||
`Provided debounce value ${debounceTimeoutMs} must be a non-negative integer`, | ||
); | ||
} | ||
|
||
const timeoutIdRef = useRef<number | null>(null); | ||
const timeoutIdRef = useRef<number | undefined>(undefined); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated ref type to match the type used by the browser's |
||
const cancelDebounce = useCallback(() => { | ||
if (timeoutIdRef.current !== null) { | ||
if (timeoutIdRef.current !== undefined) { | ||
window.clearTimeout(timeoutIdRef.current); | ||
} | ||
|
||
timeoutIdRef.current = null; | ||
timeoutIdRef.current = undefined; | ||
}, []); | ||
|
||
const debounceTimeRef = useRef(debounceTimeMs); | ||
const debounceTimeRef = useRef(debounceTimeoutMs); | ||
useEffect(() => { | ||
cancelDebounce(); | ||
debounceTimeRef.current = debounceTimeMs; | ||
}, [cancelDebounce, debounceTimeMs]); | ||
debounceTimeRef.current = debounceTimeoutMs; | ||
}, [cancelDebounce, debounceTimeoutMs]); | ||
|
||
const callbackRef = useRef(callback); | ||
useEffect(() => { | ||
|
@@ -85,31 +85,31 @@ export function useDebouncedFunction< | |
* Takes any value, and returns out a debounced version of it. | ||
*/ | ||
export function useDebouncedValue<T>(value: T, debounceTimeoutMs: number): T { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the default parameter here because if you, the user of the hook, don't know the type of a value you're trying to debounce, you're probably doing something really wrong |
||
if (!Number.isInteger(debounceTimeoutMs) || debounceTimeoutMs < 0) { | ||
throw new Error( | ||
`Provided debounce value ${debounceTimeoutMs} is not a non-negative integer`, | ||
); | ||
} | ||
if (!Number.isInteger(debounceTimeoutMs) || debounceTimeoutMs < 0) { | ||
throw new Error( | ||
`Provided debounce value ${debounceTimeoutMs} must be a non-negative integer`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here |
||
); | ||
} | ||
|
||
const [debouncedValue, setDebouncedValue] = useState(value); | ||
const [debouncedValue, setDebouncedValue] = useState(value); | ||
|
||
// If the debounce timeout is ever zero, synchronously flush any state syncs. | ||
// Doing this mid-render instead of in useEffect means that we drastically cut | ||
// down on needless re-renders, and we also avoid going through the event loop | ||
// to do a state sync that is *intended* to happen immediately | ||
if (value !== debouncedValue && debounceTimeoutMs === 0) { | ||
setDebouncedValue(value); | ||
} | ||
useEffect(() => { | ||
if (debounceTimeoutMs === 0) { | ||
return; | ||
} | ||
// If the debounce timeout is ever zero, synchronously flush any state syncs. | ||
// Doing this mid-render instead of in useEffect means that we drastically cut | ||
// down on needless re-renders, and we also avoid going through the event loop | ||
// to do a state sync that is *intended* to happen immediately | ||
if (value !== debouncedValue && debounceTimeoutMs === 0) { | ||
setDebouncedValue(value); | ||
} | ||
useEffect(() => { | ||
if (debounceTimeoutMs === 0) { | ||
return; | ||
} | ||
|
||
const timeoutId = window.setTimeout(() => { | ||
setDebouncedValue(value); | ||
}, debounceTimeoutMs); | ||
return () => window.clearTimeout(timeoutId); | ||
}, [value, debounceTimeoutMs]); | ||
const timeoutId = window.setTimeout(() => { | ||
setDebouncedValue(value); | ||
}, debounceTimeoutMs); | ||
return () => window.clearTimeout(timeoutId); | ||
}, [value, debounceTimeoutMs]); | ||
|
||
return debouncedValue; | ||
return debouncedValue; | ||
} |
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'm not sure this wording makes much sense. how about something like...