【发布时间】:2026-01-09 18:00:02
【问题描述】:
我有以下代码:
import React, { useState, useEffect } from "react"
function callSearchApi(userName: string, searchOptions: SearchOptions, searchQuery: string): Promise<SearchResult>{
return new Promise((resolve, reject) => {
const searchResult =
searchOptions.fooOption
? ["Foo 1", "Foo 2", "Foo 3"]
: ["Bar 1", "Bar 2"]
setTimeout(()=>resolve(searchResult), 3000)
})
}
type SearchOptions = {
fooOption: boolean
}
type SearchResult = string[]
export type SearchPageProps = {
userName: string
}
export function SearchPage(props: SearchPageProps) {
const [isSearching, setIsSearching] = useState<boolean>(false)
const [searchResult, setSearchResult] = useState<SearchResult>([])
const [searchOptions, setSearchOptions] = useState<SearchOptions>({fooOption: false})
const [searchQuery, setSearchQuery] = useState<string>("")
const [lastSearchButtonClickTimestamp, setLastSearchButtonClickTimestamp] = useState<number>(Date.now())
// ####################
useEffect(() => {
setIsSearching(true)
setSearchResult([])
const doSearch = () => callSearchApi(props.userName, searchOptions, searchQuery)
doSearch().then(newSearchResult => {
setSearchResult(newSearchResult)
setIsSearching(false)
}).catch(error => {
console.log(error)
setIsSearching(false)
})
}, [lastSearchButtonClickTimestamp])
// ####################
const handleSearchButtonClick = () => {
setLastSearchButtonClickTimestamp(Date.now())
}
return (
<div>
<div>
<label>
<input
type="checkbox"
checked={searchOptions.fooOption}
onChange={ev => setSearchOptions({fooOption: ev.target.checked})}
/>
Foo Option
</label>
</div>
<div>
<input
type="text"
value={searchQuery}
placeholder="Search Query"
onChange={ev => setSearchQuery(ev.target.value)}
/>
</div>
<div>
<button onClick={handleSearchButtonClick} disabled={isSearching}>
{isSearching ? "searching..." : "Search"}
</button>
</div>
<hr/>
<div>
<label>Search Result: </label>
<input
type="text"
readOnly={true}
value={searchResult}
/>
</div>
</div>
)
}
export default SearchPage
代码运行良好。我可以更改文本字段中的搜索查询并单击选项复选框。一旦我准备好了,我可以点击“搜索”按钮,然后才会通过获取数据产生副作用。
现在,问题是编译器抱怨:
React Hook useEffect 缺少依赖项:“props.user.loginName”、“searchFilter”和“searchQuery”。要么包含它们,要么删除依赖数组。 [react-hooks/exhaustive-deps]
但是,如果我将props.user.loginName、searchFilter 和searchQuery 添加到依赖项列表中,那么每当我单击复选框或在文本字段中键入单个字符时都会触发副作用。
我确实了解钩子依赖的概念,但是我不知道如何先输入一些数据,只有单击按钮才能触发副作用。
这方面的最佳做法是什么?我已经阅读了https://reactjs.org/docs/hooks-effect.html 和https://www.robinwieruch.de/react-hooks-fetch-data,但找不到任何关于我的问题的例子。
更新 1:
我还想出了this solution,它看起来像:
type DoSearch = {
call: ()=>Promise<SearchResult>
}
export function SearchPage(props: SearchPageProps) {
const [isSearching, setIsSearching] = useState<boolean>(false)
const [searchResult, setSearchResult] = useState<SearchResult>([])
const [searchOptions, setSearchOptions] = useState<SearchOptions>({fooOption: false})
const [searchQuery, setSearchQuery] = useState<string>("")
const [doSearch, setDoSearch] = useState<DoSearch>()
// ####################
useEffect(() => {
if(doSearch !==undefined){
setIsSearching(true)
setSearchResult([])
doSearch.call().then(newSearchResult => {
setSearchResult(newSearchResult)
setIsSearching(false)
}).catch(error => {
console.log(error)
setIsSearching(false)
})
}
}, [doSearch])
// ####################
const handleSearchButtonClick = () => {
setDoSearch({call: () => callSearchApi(props.userName, searchOptions, searchQuery)})
}
return (<div>...</div>)
}
现在实际函数是唯一可以正常工作的依赖项,编译器也很高兴。
但是,我不喜欢的是,我需要具有 call 属性的包装器对象。
如果我想将箭头函数直接传递给状态,这不会按预期工作,例如:
const [doSearch, setDoSearch] = useState<()=>Promise<SearchResult>>()
...
setDoSearch(() => callSearchApi(props.userName, searchOptions, searchQuery))
doSearch 没有设置为箭头函数,但callSearchApi 被立即执行。有人知道为什么吗?
【问题讨论】:
标签: reactjs typescript react-hooks