GraphQLSchemaPatternsSkill graphql-schema

这是一个关于GraphQL查询、变更和代码生成的最佳实践指南,包括文件结构、创建查询和变更的步骤、UI要求、查询选项、乐观更新和片段的使用,以及如何避免常见的错误模式。

前端开发 0 次安装 0 次浏览 更新于 3/3/2026

name: graphql-schema description: GraphQL查询,变更和代码生成模式。在创建GraphQL操作,使用Apollo客户端或生成类型时使用。

GraphQL Schema模式

核心规则

  1. 永不内联gql字面量 - 创建.gql文件
  2. 创建/修改.gql文件后 总是运行代码生成
  3. 总是添加onError处理程序到变更
  4. 使用生成的钩子 - 永不编写原始Apollo钩子

文件结构

src/
├── components/
│   └── ItemList/
│       ├── ItemList.tsx
│       ├── GetItems.gql           # 查询定义
│       └── GetItems.generated.ts  # 自动生成(不要编辑)
└── graphql/
    └── mutations/
        └── CreateItem.gql         # 共享变更

创建查询

第1步:创建.gql文件

# src/components/ItemList/GetItems.gql
查询 GetItems($limit: Int, $offset: Int) {
  项目(limit: $limit, offset: $offset) {
    id
    name
    description
    createdAt
  }
}

第2步:运行代码生成

npm run gql:typegen

第3步:导入和使用生成的钩子

import { useGetItemsQuery } from './GetItems.generated';

const ItemList = () => {
  const { data, loading, error, refetch } = useGetItemsQuery({
    variables: { limit: 20, offset: 0 },
  });

  if (error) return <ErrorState error={error} onRetry={refetch} />;
  if (loading && !data) return <LoadingSkeleton />;
  if (!data?.items.length) return <EmptyState />;

  return <List items={data.items} />;
};

创建变更

第1步:创建.gql文件

# src/graphql/mutations/CreateItem.gql
变更 CreateItem($input: CreateItemInput!) {
  创建项目(input: $input) {
    id
    name
    description
  }
}

第2步:运行代码生成

npm run gql:typegen

第3步:使用所需的错误处理

import { useCreateItemMutation } from 'graphql/mutations/CreateItem.generated';

const CreateItemForm = () => {
  const [createItem, { loading }] = useCreateItemMutation({
    // 成功处理
    onCompleted: (data) => {
      toast.success({ title: '项目已创建' });
      navigation.goBack();
    },
    // 错误处理是必需的
    onError: (error) => {
      console.error('createItem failed:', error);
      toast.error({ title: '无法创建项目' });
    },
    // 缓存更新
    update: (cache, { data }) => {
      if (data?.createItem) {
        cache.modify({
          fields: {
            items: (existing = []) => [...existing, data.createItem],
          },
        });
      }
    },
  });

  return (
    <Button
      onPress={() => createItem({ variables: { input: formValues } })}
      isDisabled={!isValid || loading}
      isLoading={loading}
    >
      创建
    </Button>
  );
};

变更UI要求

关键:每个变更触发器必须:

  1. 在变更期间禁用 - 防止双击
  2. 显示加载状态 - 视觉反馈
  3. 有onError处理程序 - 用户知道失败了
  4. 显示成功反馈 - 用户知道成功了
// 正确 - 完整的变更模式
const [submit, { loading }] = useSubmitMutation({
  onError: (error) => {
    console.error('submit failed:', error);
    toast.error({ title: '保存失败' });
  },
  onCompleted: () => {
    toast.success({ title: '已保存' });
  },
});

<Button
  onPress={handleSubmit}
  isDisabled={!isValid || loading}
  isLoading={loading}
>
  提交
</Button>

查询选项

抓取策略

策略 使用时
cache-first 数据很少变化
cache-and-network 想要快速+新鲜(默认)
network-only 总是需要最新的
no-cache 从不缓存(罕见)

常见选项

useGetItemsQuery({
  variables: { id: itemId },

  // 抓取策略
  fetchPolicy: 'cache-and-network',

  // 网络状态变化时重新渲染
  notifyOnNetworkStatusChange: true,

  // 如果条件不满足则跳过
  skip: !itemId,

  // 轮询更新
  pollInterval: 30000,
});

乐观更新

为了即时UI反馈:

const [toggleFavorite] = useToggleFavoriteMutation({
  optimisticResponse: {
    toggleFavorite: {
      __typename: 'Item',
      id: itemId,
      isFavorite: !currentState,
    },
  },
  onError: (error) => {
    // 自动回滚
    console.error('toggleFavorite failed:', error);
    toast.error({ title: '更新失败' });
  },
});

何时不使用乐观更新

  • 可以通过验证失败的操作
  • 有服务器生成值的操作
  • 破坏性操作(删除)
  • 影响其他用户的操作

片段

用于可重用的字段选择:

# src/graphql/fragments/ItemFields.gql
片段 ItemFields on Item {
  id
  name
  description
  createdAt
  updatedAt
}

在查询中使用:

查询 GetItems {
  项目 {
    ...ItemFields
  }
}

反模式

// 错误 - 内联gql
const GET_ITEMS = gql`
  查询 GetItems { 项目 { id } }
`;

// 正确 - 使用.gql文件+生成的钩子
import { useGetItemsQuery } from './GetItems.generated';


// 错误 - 没有错误处理程序
const [mutate] = useMutation(MUTATION);

// 正确 - 总是处理错误
const [mutate] = useMutation(MUTATION, {
  onError: (error) => {
    console.error('mutation failed:', error);
    toast.error({ title: '操作失败' });
  },
});


// 错误 - 按钮在变更期间不禁用
<Button onPress={submit}>提交</Button>

// 正确 - 禁用和加载
<Button onPress={submit} isDisabled={loading} isLoading={loading}>
  提交
</Button>

代码生成命令

# 从.gql文件生成类型
npm run gql:typegen

# 下载架构 + 生成类型
npm run sync-types

与其他技能的集成

  • react-ui-patterns: 查询的加载/错误/空状态
  • testing-patterns: 在测试中模拟生成的钩子
  • formik-patterns: 变更提交模式