Skip to content

Commit 8604335

Browse files
committed
Correctly ovserve input value
1 parent 4320bf6 commit 8604335

File tree

5 files changed

+75
-104
lines changed

5 files changed

+75
-104
lines changed

src/SearchBar/SearchBar.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ export const SearchBar = memo(
7878
"type": "search",
7979
"id": inputId
8080
})}
81-
{Date.now() === 0 && (
82-
<SearchButton searchInputId={inputId} onClick={onButtonClick} />
83-
)}
81+
<SearchButton searchInputId={inputId} onClick={onButtonClick} />
8482
</div>
8583
);
8684
})

src/SearchBar/SearchButton.tsx

Lines changed: 25 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { fr } from "../fr";
66
import { assert } from "tsafe/assert";
77
import { is } from "tsafe/is";
88
import { useConstCallback } from "../tools/powerhooks/useConstCallback";
9+
import { observeInputValue } from "../tools/observeInputValue";
10+
import { id } from "tsafe/id";
911

1012
export type SearchButtonProps = {
1113
searchInputId: string;
@@ -19,16 +21,17 @@ export function SearchButton(props: SearchButtonProps) {
1921

2022
const [, forceUpdate] = useReducer(x => x + 1, 0);
2123

22-
const [getInputValue, setGetInputValue] = useState(() => () => "");
23-
24-
const [resetInputValue, setResetInputValue] = useState<() => void>(() => () => {
25-
/* do nothing */
26-
});
27-
const [focusInputElement, setFocusInputElement] = useState<() => void>(() => () => {
28-
/* do nothing */
29-
});
30-
31-
const [isInputFocused, setIsInputFocused] = useState(false);
24+
const [{ focusInputElement, getInputValue, resetInputValue, getIsInputFocused }, setInputApi] =
25+
useState(() => ({
26+
"getInputValue": id<() => string>(() => ""),
27+
"resetInputValue": id<() => void>(() => {
28+
/* do nothing */
29+
}),
30+
"focusInputElement": id<() => void>(() => {
31+
/* do nothing */
32+
}),
33+
"getIsInputFocused": id<() => boolean>(() => false)
34+
}));
3235

3336
const onClick = useConstCallback(() => {
3437
const inputValue = getInputValue();
@@ -55,54 +58,22 @@ export function SearchButton(props: SearchButtonProps) {
5558

5659
assert(is<HTMLInputElement>(inputElement));
5760

58-
setGetInputValue(() => () => inputElement.value);
59-
60-
const cleanups: (() => void)[] = [];
61-
62-
inputElement.addEventListener(
63-
"input",
64-
(() => {
65-
const callback = () => {
66-
forceUpdate();
67-
};
68-
69-
cleanups.push(() => inputElement.removeEventListener("input", callback));
70-
71-
return callback;
72-
})()
73-
);
74-
75-
inputElement.addEventListener(
76-
"keydown",
77-
(() => {
78-
const callback = (event: KeyboardEvent) => {
79-
if (event.key !== "Escape") {
80-
return;
81-
}
82-
83-
forceUpdate();
84-
};
61+
setInputApi({
62+
"focusInputElement": () => inputElement.focus(),
63+
"getInputValue": () => inputElement.value,
64+
"resetInputValue": () => (inputElement.value = ""),
65+
"getIsInputFocused": () => document.activeElement === inputElement
66+
});
8567

86-
cleanups.push(() => inputElement.removeEventListener("keydown", callback));
87-
88-
return callback;
89-
})()
90-
);
68+
observeInputValue(inputElement, () => forceUpdate());
9169

92-
const resetInputValue = () => {
93-
inputElement.value = "";
94-
inputElement.dispatchEvent(new Event("input"));
95-
};
96-
97-
setResetInputValue(() => resetInputValue);
98-
99-
setFocusInputElement(() => () => inputElement.focus());
70+
const cleanups: (() => void)[] = [];
10071

10172
if (isControlledByUser) {
10273
inputElement.addEventListener(
10374
"focus",
10475
(() => {
105-
const callback = () => setIsInputFocused(true);
76+
const callback = () => forceUpdate();
10677

10778
cleanups.push(() => inputElement.removeEventListener("focus", callback));
10879

@@ -113,7 +84,7 @@ export function SearchButton(props: SearchButtonProps) {
11384
inputElement.addEventListener(
11485
"blur",
11586
(() => {
116-
const callback = () => setIsInputFocused(false);
87+
const callback = () => forceUpdate();
11788

11889
cleanups.push(() => inputElement.removeEventListener("blur", callback));
11990

@@ -149,7 +120,7 @@ export function SearchButton(props: SearchButtonProps) {
149120
return;
150121
}
151122

152-
resetInputValue();
123+
inputElement.value = "";
153124
inputElement.blur();
154125
};
155126

@@ -165,7 +136,7 @@ export function SearchButton(props: SearchButtonProps) {
165136
};
166137
}, [searchInputId, isControlledByUser]);
167138

168-
if (onClick_props === undefined && (isInputFocused || getInputValue() !== "")) {
139+
if (onClick_props === undefined && (getIsInputFocused() || getInputValue() !== "")) {
169140
return null;
170141
}
171142

src/tools/observeInputValue.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export function observeInputValue(element: HTMLInputElement, callback: (value: string) => void) {
2+
const elementPrototype = Object.getPrototypeOf(element);
3+
const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, "value");
4+
5+
Object.defineProperty(element, "value", {
6+
get: function (...args) {
7+
// @ts-expect-error
8+
return descriptor.get.apply(this, args);
9+
},
10+
set: function (...args) {
11+
// @ts-expect-error
12+
descriptor.set.apply(this, args);
13+
const newValue = this.value;
14+
15+
callback(newValue);
16+
17+
return newValue;
18+
}
19+
});
20+
}

stories/Header.stories.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -276,30 +276,21 @@ function MySearchInput(props: MySearchInputProps) {
276276
] = useState<HTMLInputElement | null>(null);
277277
278278
return (
279-
<>
280-
<input
281-
ref={setInputElement}
282-
className={className}
283-
id={id}
284-
placeholder={placeholder}
285-
type={type}
286-
value={search}
287-
onChange={event => onSearchChange(event.currentTarget.value)}
288-
onKeyDown={event => {
289-
if (event.key === "Escape") {
290-
onSearchChange("");
291-
inputElement?.blur();
292-
}
293-
}}
294-
/>
295-
<p
296-
style={{
297-
"position": "absolute",
298-
"top": 120,
299-
"left": 0
300-
}}
301-
>Search results for: {search}</p>
302-
</>
279+
<input
280+
ref={setInputElement}
281+
className={className}
282+
id={id}
283+
placeholder={placeholder}
284+
type={type}
285+
value={search}
286+
onChange={event => onSearchChange(event.currentTarget.value)}
287+
onKeyDown={event => {
288+
if (event.key === "Escape") {
289+
onSearchChange("");
290+
inputElement?.blur();
291+
}
292+
}}
293+
/>
303294
);
304295
305296
}

stories/SearchBar.stories.tsx

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -126,29 +126,20 @@ function MySearchInput(props: MySearchInputProps) {
126126
] = useState<HTMLInputElement | null>(null);
127127
128128
return (
129-
<>
130-
<input
131-
className={className}
132-
id={id}
133-
placeholder={placeholder}
134-
type={type}
135-
value={search}
136-
onChange={event => onSearchChange(event.currentTarget.value)}
137-
onKeyDown={event => {
138-
if (event.key === "Escape") {
139-
onSearchChange("");
140-
setInputElement?.blur();
141-
}
142-
}}
143-
/>
144-
<p
145-
style={{
146-
"position": "absolute",
147-
"top": 120,
148-
"left": 0
149-
}}
150-
>Search results for: {search}</p>
151-
</>
129+
<input
130+
className={className}
131+
id={id}
132+
placeholder={placeholder}
133+
type={type}
134+
value={search}
135+
onChange={event => onSearchChange(event.currentTarget.value)}
136+
onKeyDown={event => {
137+
if (event.key === "Escape") {
138+
onSearchChange("");
139+
setInputElement?.blur();
140+
}
141+
}}
142+
/>
152143
);
153144
154145
}

0 commit comments

Comments
 (0)