2025-01-10 17:20:44 +00:00
|
|
|
import { debounce } from 'lodash';
|
2023-02-22 20:13:33 +00:00
|
|
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
2022-01-04 12:16:09 +00:00
|
|
|
|
2025-01-10 17:20:44 +00:00
|
|
|
// `useRef` to keep the debouncer function (result of the _.debounce call) between rerenders.
|
|
|
|
//
|
|
|
|
// debouncer func is (value, onChange) => { onChange(value) };
|
|
|
|
//
|
|
|
|
// Previously written and used as
|
|
|
|
// const onChangeDebouncer = useRef(debounce(onChange, 300));
|
|
|
|
// onChangeDebouncer.current(value)
|
|
|
|
//
|
|
|
|
// The issue with the previous syntax is that it was holding the initial state of the `onChange` function passed to `useDebounce()`.
|
|
|
|
// When the `onChange` function was using a dynamic context (vars in parent scope/not in its parameters)
|
|
|
|
// then invoking the debouncer was producing a result of `onChange` computed uppon the initial state of the function, not the current state.
|
|
|
|
//
|
|
|
|
// Example of the issue
|
|
|
|
//
|
|
|
|
// function Component({ value }: { value: string; }) {
|
|
|
|
//
|
|
|
|
// function onChange(v: string) {
|
|
|
|
// // This will always print the first value of the "value" prop + the updated value of "v"
|
|
|
|
// // when called from "handleChange".
|
|
|
|
// // This is an issue when the `onChange` is a prop of the component and the real function performs state mutations upflow based on
|
|
|
|
// // values that are in the parent component, as `setDebouncedValue` will only use the initial instance of the `onChange` prop, thus
|
|
|
|
// // the initial state of the parent component.
|
|
|
|
// console.log(value, v)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// const [debouncedValue, setDebouncedValue] = useDebounce(value, onChange);
|
|
|
|
//
|
|
|
|
// function handleChange(newValue: string) {
|
|
|
|
// setDebouncedValue(newValue);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return (<Input value={debouncedValue} onChange={(e) => handleChange(e.target.value)} />)
|
|
|
|
// }
|
2023-02-22 20:13:33 +00:00
|
|
|
export function useDebounce(value: string, onChange: (value: string) => void) {
|
|
|
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
2022-01-04 12:16:09 +00:00
|
|
|
|
2025-01-10 17:20:44 +00:00
|
|
|
// Do not change. See notes above
|
|
|
|
const onChangeDebouncer = useRef(
|
|
|
|
debounce(
|
|
|
|
(value: string, onChangeFunc: (v: string) => void) => onChangeFunc(value),
|
|
|
|
300
|
|
|
|
)
|
|
|
|
);
|
2022-01-04 12:16:09 +00:00
|
|
|
|
2023-02-13 02:06:14 +00:00
|
|
|
const handleChange = useCallback(
|
|
|
|
(value: string) => {
|
2023-02-22 20:13:33 +00:00
|
|
|
setDebouncedValue(value);
|
2025-01-10 17:20:44 +00:00
|
|
|
onChangeDebouncer.current(value, onChange);
|
2023-02-13 02:06:14 +00:00
|
|
|
},
|
2025-01-10 17:20:44 +00:00
|
|
|
[onChangeDebouncer, setDebouncedValue, onChange]
|
2023-02-13 02:06:14 +00:00
|
|
|
);
|
2022-01-04 12:16:09 +00:00
|
|
|
|
2023-02-22 20:13:33 +00:00
|
|
|
useEffect(() => {
|
|
|
|
setDebouncedValue(value);
|
|
|
|
}, [value]);
|
|
|
|
|
|
|
|
return [debouncedValue, handleChange] as const;
|
2022-01-04 12:16:09 +00:00
|
|
|
}
|