Skip to content

Commit 67b18c8

Browse files
committed
fxing search control to be a controlled text box
1 parent e24fd32 commit 67b18c8

File tree

1 file changed

+20
-29
lines changed

1 file changed

+20
-29
lines changed

src/controls/search.tsx

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { Input, InputProps, makeStyles, mergeClasses } from '@fluentui/react-components';
22
import { DismissRegular, SearchRegular } from "@fluentui/react-icons";
3-
import { debounce, isFunction, isNullOrEmptyString, isUndefined } from '@kwiz/common';
4-
import React, { useState } from 'react';
3+
import { debounce, isNullOrEmptyString } from '@kwiz/common';
4+
import React, { useRef } from 'react';
55
import { GetLogger } from '../_modules/config';
6-
import { useStateEX } from '../helpers';
76
import { mixins } from '../styles/styles';
87
const logger = GetLogger("Search");
98

@@ -16,55 +15,47 @@ const useStyles = makeStyles({
1615
},
1716
})
1817

19-
interface IProps extends InputProps {
18+
interface IProps extends Omit<InputProps, "onChange"> {
2019
main?: boolean;
20+
/** number of seconds to debounce the deferred event */
2121
delay?: number;
2222
/** if changing the value in the caller - change this prop to reset */
2323
resetValue?: string;
2424
onChangeDeferred?: (newValue: string) => void;
25-
onChangeSync?: (newValue: string) => void;
25+
onChange?: (newValue: string) => void;
2626
}
2727

2828
/** value is set on first load. to change the value after it was first set - change the compoenet's key. */
2929
export const Search: React.FunctionComponent<React.PropsWithChildren<IProps>> = (props) => {
3030
const cssNames = useStyles();
3131

32-
const [resetKey, setResetKey] = useState(1);
33-
3432
let delay = props.delay || 1;
3533

34+
let refonChangeDeferred = useRef(props.onChangeDeferred);
35+
//keep updating the ref
36+
React.useEffect(() => { refonChangeDeferred.current = props.onChangeDeferred; }, [props.onChangeDeferred]);
37+
3638
//cannot call debounce every render, since it won't be the same debounced instance...
3739
var notifyParent = React.useMemo(() => debounce(v => {
3840
logger.log(`Set: ${v}`);
39-
props.onChangeDeferred(v);
41+
//Call the latest ref - we don't want to call an old version of this function
42+
refonChangeDeferred.current?.(v);
4043
}, delay * 1000), [delay]);
4144

42-
let [value, setValue] = useStateEX(props.value || "", {
43-
onChange: newValue => {
44-
if (isFunction(props.onChangeSync)) props.onChangeSync(newValue as string);
45-
if (isFunction(props.onChangeDeferred)) notifyParent(newValue);
46-
return newValue;
47-
}
48-
});
49-
50-
//once props change, reset this control value to match
51-
React.useEffect(() => {
52-
if (!isUndefined(props.resetValue))
53-
setValue(props.resetValue);
54-
//todo: bug: setting value does not sync into the text box
55-
setResetKey(resetKey + 1)
56-
}, [props.resetValue]);
45+
const currentValue = props.value || "";
5746

5847
return (
59-
<Input key={resetKey} {...props} value={value} onChange={(e, data) => setValue(data.value)}
48+
<Input {...props} value={currentValue} onChange={(e, data) => {
49+
props.onChange?.(data.value);
50+
notifyParent(data.value);
51+
}}
6052
className={mergeClasses(cssNames.root, props.main && cssNames.main)}
61-
contentBefore={!isNullOrEmptyString(value) ? undefined : <SearchRegular className={cssNames.searchIcon} />}
62-
contentAfter={isNullOrEmptyString(value)
53+
contentBefore={!isNullOrEmptyString(currentValue) ? undefined : <SearchRegular className={cssNames.searchIcon} />}
54+
contentAfter={isNullOrEmptyString(currentValue)
6355
? undefined
6456
: <DismissRegular className={cssNames.clickable} onClick={() => {
65-
setValue("");
66-
//todo: bug: setting value does not sync into the text box
67-
setResetKey(resetKey + 1)
57+
props.onChange?.("");
58+
notifyParent("");
6859
}} />
6960
} />
7061
);

0 commit comments

Comments
 (0)