我创建了两个自定义挂钩
usePagination
和 useSearch
。它们处理分页和搜索的逻辑,并且仅公开当前页面和搜索查询的 getter/setter。
这是我在列表组件中使用它们的方法:
const List = () => {
const [currentPage, setCurrentPage] = usePagination()
const [searchQuery, setSearchQuery] = useSearch()
const queryParams = useMemo(() => ({
page: currentPage,
search: searchQuery
}), [currentPage, searchQuery])
// Pass the `queryParams` to an API endpoint to load my list items.
}
当搜索查询发生变化时,我需要将当前页面重置为1。像这样:
useEffect(()=>{
setCurrentPage(1)
}, [searchQuery, setCurrentPage])
当然,按照上面的逻辑,这会导致两次API调用。当搜索查询更改时一个,然后在重置当前页面值后立即另一个。
有没有办法将我的自定义挂钩
currentPage
和 searchQuery
中的两个值组合成组合状态,以便我可以同时更新页码和查询?换句话说,除了废弃我的个人钩子并为所有事情使用单一状态之外,我还能做些什么吗?
例如
const [queryState, setQueryState] = useState({
currentPage: 1,
searchQuery: ""
})
是的,你几乎做对了。你可以做这样的事情:
const [queryState, setQueryState] = useState({
currentPage: 1,
searchQuery: ""
})
setQueryState((previousState) => {
// change the state
return previousState;
})
您甚至可以将其包装到一个钩子中,该钩子采用初始状态并返回 getter 和修改后的 setter。类似于以下内容:
// hook body
const [queryState, setQueryState] = useState(initialState)
const modifiedSetter = (newValues) => {
setQueryState((previousState) => {
return {...previousState, ...newValues};
})
}
return [queryState, modifiedSetter]
它的用法是:
const [queryState, updateQueryState] = useMyCustomHook({...someinitialState})
// Well, getter works the same: queryState.searchQuery
// but the setter goes like this
updateQueryState({searchQuery: 'whatever'});
// or
updateQueryState({searchQuery: 'whatever', currentPage: 1});
您能解释一下为什么需要 setState/setCurrentPage 函数作为依赖项吗?
作为预期效果,每当 useState 值更改时,整个组件都会重新渲染,这就是您有两个 API 调用的原因,因为两个状态都会导致重新渲染。
解决方案:假设您只想在 searchQuery 更改时进行新的 API 调用,而不是在 currentPage 数据更改时进行新的 API 调用,React 提供了 useRef,您可以使用它来存储/更新数据,而无需重新渲染并进行昂贵的 API 调用。
示例(在您的 usePagination 挂钩中):
function usePagination() {
const currentPage = useRef(0);
//make API call...
//if (currentPage.current === 0) {...}
return currentPage
}
然后在列表中:
const List = () => {
const currentPage = usePagination()
useEffect(()=>{
currentPage.current = 1
}, [searchQuery])
//...
}