export default function SearchPage() {
const [searchString, setSearchString] = React.useState("");
const [apiCall, setApiCall] = React.useState<() => Promise<Collection>>();
const {isIdle, isLoading, isError, error, data} = useApi(apiCall);
const api = useContext(ApiContext);
useEffect(()=>console.log("APICall changed to", apiCall), [apiCall]);
const doSearch = (event: React.FormEvent) => {
event.preventDefault();
setApiCall(() => () => api.search(searchString));
};
const doNext = () => {
var next = api.next;
if (next) {
setApiCall(()=>(() => next)());
}
window.scrollTo(0, 0);
}
const doPrev = () => {
if (api.prev) {
setApiCall(() => api.prev);
}
window.scrollTo(0, 0);
}
return (
<>
<form className={"searchBoxContainer"} onSubmit={doSearch}>
<TextField
label={"Search"}
variant={"filled"}
value={searchString}
onChange={handleChange}
className={"searchBox"}
InputProps={{
endAdornment: (
<IconButton onClick={() => setSearchString("")}>
<ClearIcon/>
</IconButton>
)
}}
/>
<Button type={"submit"} variant={"contained"} className={"searchButton"}>Go</Button>
</form>
{
(isIdle) ? (
<span/>
) : isLoading ? (
<span>Loading...</span>
) : isError ? (
<span>Error: {error}</span>
) : (
<Paper className={"searchResultsContainer"}>
<Box className={"navButtonContainer"}>
<Button variant={"contained"}
disabled={!api.prev}
onClick={doPrev}
className={"navButton"}>
{"< Prev"}
</Button>
<Button variant={"contained"}
disabled={!api.next}
onClick={doNext}
className={"navButton"}>
{"Next >"}
</Button>
</Box>
<Box className={"searchResults"}>
{
data && data.items().all().map(item => (
<span className={"thumbnailWrapper"}>
<img className={"thumbnail"}
src={item.link("preview")?.href}
alt={(Array.from(item.allData())[0].object as SearchResponseDataModel).title}/>
</span>
))
}
</Box>
<Box className={"navButtonContainer"}>
<Button variant={"contained"}
disabled={!api.prev}
onClick={doPrev}
className={"navButton"}>
{"< Prev"}
</Button>
<Button variant={"contained"}
disabled={!api.next}
onClick={doNext}
className={"navButton"}>
{"Next >"}
</Button>
</Box>
</Paper>
)
}
</>
)
}
由于各种原因,我在我的状态中存储了一个函数(它与react-query
库一起使用)。不过,当我尝试更新它时,我看到了非常奇怪的行为。当任何doSearch
,doNext
或doPrev
被调用时,它会成功更新状态 -useEffect
钩子正在正确触发,我可以在控制台中看到消息 - 但它不会触发重新渲染,直到窗口失去并重新获得焦点。
我见过的大多数其他人都在以他们的状态存储一个数组,并更新数组而不是创建一个新的数组——所以钩子不会把它当作一个新对象,并且重新渲染不会发生。不过,我没有使用数组,我使用的是一个函数,并将不同的函数对象传递给它。我完全被难住了,不知道发生了什么。
编辑:似乎它可能不是渲染失败,但查询钩子没有注意到它的输入已经改变?我已经编辑了上面的代码以显示整个函数,下面是我的自定义钩子。
function useApi(func?: () => Promise<Collection>) {
return useQuery(
["doApiCall", func],
func || (async () => await undefined),
{
enabled: !!func,
keepPreviousData: true
}
)
}