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

React GraphQL:能否用switch语句切换多个useLazyQuery?

在React GraphQL中切换多个useLazyQuery的可行方案

首先明确:你不能像代码示例那样在switch语句(或其他条件判断、嵌套函数)里调用useLazyQuery——这违反了React Hooks的核心规则:必须在组件的顶层作用域调用Hooks,不能在条件分支、循环、嵌套函数内调用。React需要依赖Hooks的调用顺序来维护状态,条件内调用会破坏这个顺序,导致不可预测的bug。

不过你想要的「用同一逻辑切换不同查询、避免拆分组件」的需求完全可以实现,下面是两种靠谱的方案:

方案1:提前声明所有useLazyQuery,按类型映射选择

先在组件顶层声明所有需要的useLazyQuery,再通过对象映射或switch,根据当前listType获取对应的查询函数和状态。

import { useLazyQuery } from '@apollo/client';
// 导入四个查询文档
import LIST_ONE_QUERY from './queries/ListOne.graphql';
import LIST_TWO_QUERY from './queries/ListTwo.graphql';
import LIST_THREE_QUERY from './queries/ListThree.graphql';
import LIST_FOUR_QUERY from './queries/ListFour.graphql';

function TabbedList() {
  const [listType, setListType] = useState("List One");

  // 提前声明所有useLazyQuery(遵守Hooks规则)
  const [getListOne, { data: dataOne, loading: loadingOne, error: errorOne, refetch: refetchOne }] = useLazyQuery(LIST_ONE_QUERY);
  const [getListTwo, { data: dataTwo, loading: loadingTwo, error: errorTwo, refetch: refetchTwo }] = useLazyQuery(LIST_TWO_QUERY);
  const [getListThree, { data: dataThree, loading: loadingThree, error: errorThree, refetch: refetchThree }] = useLazyQuery(LIST_THREE_QUERY);
  const [getListFour, { data: dataFour, loading: loadingFour, error: errorFour, refetch: refetchFour }] = useLazyQuery(LIST_FOUR_QUERY);

  // 用对象映射关联类型与查询资源
  const queryMap = {
    "List One": { getData: getListOne, data: dataOne, loading: loadingOne, error: errorOne, refetch: refetchOne },
    "List Two": { getData: getListTwo, data: dataTwo, loading: loadingTwo, error: errorTwo, refetch: refetchTwo },
    "List Three": { getData: getListThree, data: dataThree, loading: loadingThree, error: errorThree, refetch: refetchThree },
    "List Four": { getData: getListFour, data: dataFour, loading: loadingFour, error: errorFour, refetch: refetchFour }
  };

  // 根据当前类型获取对应的查询函数和状态
  const { getData, data, loading, error, refetch } = queryMap[listType] || queryMap["List One"];

  // 标签点击逻辑:切换类型并触发对应查询
  const handleTabClick = (type) => {
    setListType(type);
    queryMap[type].getData({ variables: {} });
  };

  return (
    <div>
      {/* 标签页区域 */}
      <div className="tabs">
        <button onClick={() => handleTabClick("List One")}>列表1</button>
        <button onClick={() => handleTabClick("List Two")}>列表2</button>
        <button onClick={() => handleTabClick("List Three")}>列表3</button>
        <button onClick={() => handleTabClick("List Four")}>列表4</button>
      </div>
      {/* 统一数据展示 */}
      {loading && <div>加载中...</div>}
      {error && <div>出错了:{error.message}</div>}
      {data && (
        <table>
          <thead>
            <tr>
              <th>列1</th>
              <th>列2</th>
              {/* 其他统一列 */}
            </tr>
          </thead>
          <tbody>
            {/* 假设所有查询返回的列表字段一致,比如data.items */}
            {data.items.map(item => (
              <tr key={item.id}>
                <td>{item.field1}</td>
                <td>{item.field2}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

方案2:单个useLazyQuery动态传入查询文档

如果四个查询的返回数据结构完全一致,只是查询语句不同,可以把查询文档存在映射对象里,根据listType动态选择,只用一个useLazyQuery即可。

import { useLazyQuery } from '@apollo/client';
import LIST_ONE_QUERY from './queries/ListOne.graphql';
import LIST_TWO_QUERY from './queries/ListTwo.graphql';
import LIST_THREE_QUERY from './queries/ListThree.graphql';
import LIST_FOUR_QUERY from './queries/ListFour.graphql';

function TabbedList() {
  const [listType, setListType] = useState("List One");

  // 映射类型与查询文档
  const queryDocMap = {
    "List One": LIST_ONE_QUERY,
    "List Two": LIST_TWO_QUERY,
    "List Three": LIST_THREE_QUERY,
    "List Four": LIST_FOUR_QUERY
  };

  // 初始化useLazyQuery,用当前类型对应的查询文档
  const [getData, { data, loading, error, refetch }] = useLazyQuery(queryDocMap[listType]);

  // 标签点击逻辑:切换类型并传入新的查询文档触发查询
  const handleTabClick = (type) => {
    setListType(type);
    getData({ 
      query: queryDocMap[type],
      variables: {} 
    });
  };

  return (
    {/* 标签页和数据展示逻辑与方案1一致 */}
    <div>
      <div className="tabs">
        <button onClick={() => handleTabClick("List One")}>列表1</button>
        <button onClick={() => handleTabClick("List Two")}>列表2</button>
        <button onClick={() => handleTabClick("List Three")}>列表3</button>
        <button onClick={() => handleTabClick("List Four")}>列表4</button>
      </div>
      {loading && <div>加载中...</div>}
      {error && <div>出错了:{error.message}</div>}
      {data && (
        <table>
          <thead>
            <tr>
              <th>列1</th>
              <th>列2</th>
            </tr>
          </thead>
          <tbody>
            {data.items.map(item => (
              <tr key={item.id}>
                <td>{item.field1}</td>
                <td>{item.field2}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

方案对比

  • 方案1的优势是每个查询的状态独立,切换标签时不会丢失之前的查询结果(若需要保留历史数据);
  • 方案2更简洁,但切换标签后之前的data会被新查询覆盖,适合不需要保留历史数据的场景;
  • 若查询变量需要随listType变化,只需在调用getData时传入对应variables即可。

内容的提问来源于stack exchange,提问作者Danny Thomas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 03:36:19