@samitouri / QOS-React / commits / e2ba45bb39

[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 );