name: graphql-schema description: GraphQL查询,变更和代码生成模式。在创建GraphQL操作,使用Apollo客户端或生成类型时使用。
GraphQL Schema模式
核心规则
- 永不内联
gql字面量 - 创建.gql文件 - 创建/修改
.gql文件后 总是运行代码生成 - 总是添加
onError处理程序到变更 - 使用生成的钩子 - 永不编写原始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要求
关键:每个变更触发器必须:
- 在变更期间禁用 - 防止双击
- 显示加载状态 - 视觉反馈
- 有onError处理程序 - 用户知道失败了
- 显示成功反馈 - 用户知道成功了
// 正确 - 完整的变更模式
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: 变更提交模式