Skip to content

Commit 00432fa

Browse files
feat: use sync external store to avoid React gymnastics
before we have to use ref and tricky dep
1 parent 4b2fa93 commit 00432fa

File tree

2 files changed

+39
-36
lines changed

2 files changed

+39
-36
lines changed

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type Definition = {
2626
searchResultStreaming: {
2727
searchResult: SgSearch[]
2828
id: number
29+
inputValue: string
2930
}
3031
searchEnd: {
3132
id: number

src/webview/hooks/useSearch.tsx

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,42 @@
11
import type { SgSearch } from '../postMessage'
22
import { childPort } from '../postMessage'
3-
import { useCallback, useMemo, useState, useRef } from 'react'
3+
import { useCallback, useMemo, useSyncExternalStore } from 'react'
44
import { useDebounce } from 'react-use'
55

6-
type StreamHandler = (r: SgSearch[]) => void
7-
86
// id should not overflow, the MOD is large enough
97
// for most cases (unless there is buggy search)
108
const MOD = 1e9 + 7
119

1210
// maintain the latest search task id and callback
1311
let id = 0
14-
let currentResolve: (id: number) => void
15-
let currentReject = () => {}
16-
let currentHandler: StreamHandler
12+
let searchResult: SgSearch[] = []
13+
let queryInFlight = ''
14+
let searching = true
15+
let notify = () => {}
1716

18-
function postSearch(inputValue: string, onStream: StreamHandler) {
17+
function postSearch(inputValue: string) {
1918
id = (id + 1) % MOD
2019
childPort.postMessage('search', { id, inputValue })
21-
currentReject()
22-
currentHandler = onStream
23-
return new Promise<number>((resolve, reject) => {
24-
currentResolve = resolve
25-
currentReject = reject
26-
})
20+
searchResult = []
21+
searching = true
22+
notify()
2723
}
2824

2925
childPort.onMessage('searchResultStreaming', event => {
3026
if (event.id !== id) {
3127
return
3228
}
33-
currentHandler(event.searchResult)
29+
queryInFlight = event.inputValue
30+
searchResult = searchResult.concat(event.searchResult)
31+
notify()
3432
})
33+
3534
childPort.onMessage('searchEnd', event => {
3635
if (event.id !== id) {
3736
return
3837
}
39-
currentResolve(id)
40-
currentResolve = () => {}
41-
currentReject = () => {}
42-
currentHandler = () => {}
38+
searching = false
39+
notify()
4340
})
4441

4542
function groupBy(matches: SgSearch[]) {
@@ -53,36 +50,41 @@ function groupBy(matches: SgSearch[]) {
5350
return groups
5451
}
5552

53+
// version is for react to update view
54+
let version = 114514
55+
function subscribe(onChange: () => void): () => void {
56+
notify = () => {
57+
onChange()
58+
version = (version + 1) % MOD
59+
}
60+
return () => {
61+
// TODO: cleanup is not correct
62+
notify = () => {}
63+
}
64+
}
65+
66+
function getSnapshot() {
67+
return version // symbolic snapshot for react
68+
}
69+
5670
export const useSearchResult = (inputValue: string) => {
57-
const resultRef = useRef<SgSearch[]>([])
58-
const [searching, setSearching] = useState(false)
59-
const [queryInFlight, setQuery] = useState(inputValue)
71+
useSyncExternalStore(subscribe, getSnapshot)
6072

6173
const refreshSearchResult = useCallback(() => {
62-
setSearching(true)
63-
resultRef.current = []
64-
postSearch(inputValue, ret => {
65-
resultRef.current.push(...ret)
66-
setQuery(inputValue + resultRef.current.length)
67-
})
68-
.then(() => {
69-
setSearching(false)
70-
})
71-
.catch(() => {
72-
// TODO: cancelled request, should send cancel to extension
73-
})
74+
// TODO: cancelled request, should send cancel to extension
75+
postSearch(inputValue)
7476
}, [inputValue])
7577

7678
const groupedByFileSearchResult = useMemo(() => {
77-
return [...groupBy(resultRef.current).entries()]
78-
}, [resultRef.current.length])
79+
return [...groupBy(searchResult).entries()]
80+
}, [searchResult])
7981

8082
useDebounce(refreshSearchResult, 100, [inputValue])
8183

8284
return {
8385
queryInFlight,
8486
searching,
85-
searchResult: resultRef.current,
87+
searchResult,
8688
groupedByFileSearchResult,
8789
refreshSearchResult
8890
}

0 commit comments

Comments
 (0)