如何合并自定义Hook的状态值,避免搜索时触发两次API调用?
React自定义Hook联动导致重复API调用的解决方案
问题描述
我创建了两个自定义Hook:usePagination和useSearch,分别处理分页和搜索逻辑,仅对外暴露当前页码、搜索关键词的获取/设置方法。
在列表组件中用法如下:
const List = () => { const [currentPage, setCurrentPage] = usePagination() const [searchQuery, setSearchQuery] = useSearch() const queryParams = useMemo(() => ({ page: currentPage, search: searchQuery }), [currentPage, searchQuery]) // 将queryParams传入API加载列表项 }
当搜索关键词变化时,需要把页码重置为1,于是添加了这段逻辑:
useEffect(()=>{ setCurrentPage(1) }, [searchQuery, setCurrentPage])
但这样会触发两次API调用:一次是搜索关键词变化时,另一次是页码重置后。想知道有没有办法把两个Hook的状态合并成组合状态来同时更新,或者不用废弃现有独立Hook的前提下解决这个问题?比如类似单一状态的写法:
const [queryState, setQueryState] = useState({ currentPage: 1, searchQuery: "" })
解决方案
方法一:给自定义Hook增加联动逻辑
不用废弃现有Hook,修改useSearch,让它在搜索关键词变化时自动触发页码重置,或者提供回调参数支持联动:
function useSearch(onSearchChange) { const [searchQuery, setSearchQuery] = useState(''); const handleSetSearchQuery = (newQuery) => { setSearchQuery(newQuery); onSearchChange?.(); // 搜索变化时执行回调 }; return [searchQuery, handleSetSearchQuery]; }
组件内使用时传入页码重置逻辑:
const List = () => { const [currentPage, setCurrentPage] = usePagination() // 搜索时自动重置页码 const [searchQuery, setSearchQuery] = useSearch(() => setCurrentPage(1)) const queryParams = useMemo(() => ({ page: currentPage, search: searchQuery }), [currentPage, searchQuery]) // API调用逻辑 }
React会合并同一事件流中的状态更新,只会触发一次API请求。
方法二:优化API调用触发时机
用useRef标记状态变化的原因,避免重复调用API:
const List = () => { const [currentPage, setCurrentPage] = usePagination() const [searchQuery, setSearchQuery] = useSearch() const isResettingPage = useRef(false); // 搜索变化时标记要重置页码 useEffect(() => { isResettingPage.current = true; setCurrentPage(1); }, [searchQuery, setCurrentPage]) const queryParams = useMemo(() => ({ page: currentPage, search: searchQuery }), [currentPage, searchQuery]) // 控制API调用时机 useEffect(() => { // 搜索导致的页码重置阶段,跳过第一次请求 if (isResettingPage.current) { isResettingPage.current = false; return; } // 此处执行API调用 }, [queryParams]) }
方法三:封装组合Hook,保留原逻辑同时合并状态
创建新的组合Hook整合两个Hook的逻辑,对外提供统一的状态和更新方法:
function useListQuery() { const [currentPage, setCurrentPage] = usePagination(); const [searchQuery, setSearchQuery] = useSearch(); // 自定义方法:设置搜索词时自动重置页码 const setSearchAndResetPage = (newQuery) => { setSearchQuery(newQuery); setCurrentPage(1); }; // 组合后的统一状态 const queryState = useMemo(() => ({ currentPage, searchQuery }), [currentPage, searchQuery]); return [queryState, setCurrentPage, setSearchAndResetPage]; }
组件内使用:
const List = () => { const [queryState, setCurrentPage, setSearchAndResetPage] = useListQuery(); const queryParams = useMemo(() => ({ page: queryState.currentPage, search: queryState.searchQuery }), [queryState]) // 搜索时调用setSearchAndResetPage,分页时调用setCurrentPage }
内容的提问来源于stack exchange,提问作者user1032752
相关产品推荐
相关产品推荐

