[DevTools] fix: keep search query in a local sync state (#34423)
When the search query changes, we kick off a transition that updates the search query in a reducer for TreeContext. The search input is also using this value for an `input` HTML element. For a larger applications, sometimes there is a noticeable delay in displaying the updated search query. This changes the approach to also keep a local synchronous state that is being updated on a change callback.
Ruslan Lesiutin committed
Sep 10, 2025 at 18:38 UTC
e2ba45bb39bd744454ee599bdc2df497c79d9707
1 file changed
+26
-14
packages/react-devtools-shared/src/devtools/views/Components/ComponentSearchInput.js
+26
-14
@@ -8,22 +8,34 @@
8
*/
9
10
import * as React from 'react';
11
-import {useContext} from 'react';
12
-import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
11
+import {useState, useContext, useCallback} from 'react';
12
14
-import SearchInput from '../SearchInput';
13
+import SearchInput from 'react-devtools-shared/src/devtools/views/SearchInput';
14
+import {
15
+ TreeDispatcherContext,
16
+ TreeStateContext,
17
+} from 'react-devtools-shared/src/devtools/views/Components/TreeContext';
18
16
-type Props = {};
19
+export default function ComponentSearchInput(): React.Node {
20
+ const [localSearchQuery, setLocalSearchQuery] = useState('');
21
+ const {searchIndex, searchResults} = useContext(TreeStateContext);
22
+ const transitionDispatch = useContext(TreeDispatcherContext);
23
18
-export default function ComponentSearchInput(props: Props): React.Node {
19
- const {searchIndex, searchResults, searchText} = useContext(TreeStateContext);
20
- const dispatch = useContext(TreeDispatcherContext);
21
-
22
- const search = (text: string) =>
23
- dispatch({type: 'SET_SEARCH_TEXT', payload: text});
24
- const goToNextResult = () => dispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'});
25
- const goToPreviousResult = () =>
26
- dispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'});
24
+ const search = useCallback(
25
+ (text: string) => {
26
+ setLocalSearchQuery(text);
27
+ transitionDispatch({type: 'SET_SEARCH_TEXT', payload: text});
28
+ },
29
+ [setLocalSearchQuery, transitionDispatch],
30
+ );
31
+ const goToNextResult = useCallback(
32
+ () => transitionDispatch({type: 'GO_TO_NEXT_SEARCH_RESULT'}),
33
+ [transitionDispatch],
34
+ );
35
+ const goToPreviousResult = useCallback(
36
+ () => transitionDispatch({type: 'GO_TO_PREVIOUS_SEARCH_RESULT'}),
37
+ [transitionDispatch],
38
+ );
39
40
return (
41
<SearchInput
@@ -33,7 +45,7 @@ export default function ComponentSearchInput(props: Props): React.Node {
45
search={search}
46
searchIndex={searchIndex}
47
searchResultsCount={searchResults.length}
36
- searchText={searchText}
48
+ searchText={localSearchQuery}
49
testName="ComponentSearchInput"
50
/>
51
);