You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:25:07