mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
/*
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
* Side Public License, v 1.
|
|
*/
|
|
|
|
import { useState, useMemo, useEffect, useRef } from 'react';
|
|
import { debounce } from 'lodash';
|
|
|
|
/**
|
|
* Debounces value changes and updates inputValue on root state changes if no debounced changes
|
|
* are in flight because the user is currently modifying the value.
|
|
*
|
|
* * allowFalsyValue: update upstream with all falsy values but null or undefined
|
|
*
|
|
* When testing this function mock the "debounce" function in lodash (see this module test for an example)
|
|
*/
|
|
|
|
export const useDebouncedValue = <T>(
|
|
{
|
|
onChange,
|
|
value,
|
|
defaultValue,
|
|
}: {
|
|
onChange: (val: T) => void;
|
|
value: T;
|
|
defaultValue?: T;
|
|
},
|
|
{ allowFalsyValue }: { allowFalsyValue?: boolean } = {}
|
|
) => {
|
|
const [inputValue, setInputValue] = useState(value);
|
|
const unflushedChanges = useRef(false);
|
|
const shouldUpdateWithFalsyValue = Boolean(allowFalsyValue);
|
|
|
|
// Save the initial value
|
|
const initialValue = useRef(defaultValue ?? value);
|
|
|
|
const flushChangesTimeout = useRef<NodeJS.Timeout | undefined>();
|
|
|
|
const onChangeDebounced = useMemo(() => {
|
|
const callback = debounce((val: T) => {
|
|
onChange(val);
|
|
// do not reset unflushed flag right away, wait a bit for upstream to pick it up
|
|
flushChangesTimeout.current = setTimeout(() => {
|
|
unflushedChanges.current = false;
|
|
}, 256);
|
|
}, 256);
|
|
return (val: T) => {
|
|
if (flushChangesTimeout.current) {
|
|
clearTimeout(flushChangesTimeout.current);
|
|
}
|
|
unflushedChanges.current = true;
|
|
callback(val);
|
|
};
|
|
}, [onChange]);
|
|
|
|
useEffect(() => {
|
|
if (!unflushedChanges.current && value !== inputValue) {
|
|
setInputValue(value);
|
|
}
|
|
}, [value, inputValue]);
|
|
|
|
const handleInputChange = (val: T) => {
|
|
setInputValue(val);
|
|
const valueToUpload = shouldUpdateWithFalsyValue
|
|
? val ?? initialValue.current
|
|
: val || initialValue.current;
|
|
onChangeDebounced(valueToUpload);
|
|
};
|
|
|
|
return { inputValue, handleInputChange, initialValue: initialValue.current };
|
|
};
|