Как использовать фрагмент GraphQL на нескольких типах
Я использую Contenful в качестве источника для страниц, постов и вики-статей сайта Gatsby. Мои запросы для страниц и вики-статей выглядят очень похоже:
PageBySlug
export const pageQuery = graphql`
query PageBySlug($slug: String!) {
site {
meta: siteMetadata {
title
url: siteUrl
}
}
page: contentfulPage(slug: {eq: $slug}) {
title {
title
}
slug
body {
data: childMarkdownRemark {
excerpt
html
headings {
value
depth
}
}
}
created: createdAt(formatString: "D. MMMM YYYY", locale: "de")
updated: updatedAt(formatString: "D. MMMM YYYY", locale: "de")
authors: author {
name
email
}
}
}
`
WikiArticleBySlug
export const articleQuery = graphql`
query WikiArticleBySlug($slug: String!) {
site {
meta: siteMetadata {
title
url: siteUrl
}
}
article: contentfulWikiArticle(slug: {eq: $slug}) {
title {
title
}
slug
body {
data: childMarkdownRemark {
html
headings {
value
depth
}
}
}
created: createdAt(formatString: "D. MMMM YYYY", locale: "de")
updated: updatedAt(formatString: "D. MMMM YYYY", locale: "de")
authors: author {
name
email
}
section {
title
slug
}
subsection {
title
slug
}
}
}
`
За исключением дополнительного раздела и подраздела в вики-статьях, запросы идентичны. Чтобы сохранить вещи СУХОЙ, как я могу передать как можно больше каждого запроса в отдельный фрагмент, несмотря на то, что работал с несколькими типами?
fragment pageFields on ContenfulPage, ContenfulWikiArticle {
...
}
явно не работает.
0 ответов
Недавний выпуск Gatsby позволяет пользователям устанавливать собственные типы для схемы graphql, что делает этот вопрос окончательно возможным.
Это всегда было возможно с GraphQl, если пользователи имеют контроль над схемой, но благодаря недавнему обновлению Gatsby, пользователи, наконец, могут реализовать это самостоятельно.
Настроить
Чтобы настроить простой пример, я буду использовать gatsby-transformer-json
в такой простой папке
jsonFolder
|--one.json { "type": "One", "name": "a", "food": "pizza" }
`--two.json { "type": "Two", "name": "b", "game": "chess" }
и используйте опцию для объявления моего имени типа:
{
resolve: `gatsby-transformer-json`,
options: {
typeName: ({ object }) => object.type,
},
},
Теперь у меня есть два типа, которые были созданы для меня. Я могу создать фрагмент на одном из них, но не на обоих:
export const name = graphql`
fragment name on One {
name
}
`
export const pageQuery = graphql`
query {
one {
...name
}
two {
...name <-- ⚠️ throw type error
}
}
`
Давайте это исправим.
Настройка типов
Я буду использовать новый API под названием createTypes
зарегистрировать новый интерфейс и 2 типа для каждого из JSON. Обратите внимание, что JsonNode
содержит общие поля обоих One
а также Two
:
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
interface JsonNode {
name: String
type: String!
}
type One implements Node & JsonNode {
name: String
type: String!
food: String
}
type Two implements Node & JsonNode {
name: String
type: String!
game: String
}
`
createTypes(typeDefs)
}
Волшебство происходит на этой линии, где One
& Two
реализует оба JsonNode
(пользовательский интерфейс) и Node
(Интерфейс Гэтсби).
type One implements Node & JsonNode { ... }
Теперь я могу написать фрагмент, который реализует JsonNode
И это будет работать для обоих типов.
// blogPostTemplate.js
import React from "react"
import { graphql } from "gatsby"
export default ({ data }) => <div>{JSON.Stringify(data)}</div>
export const name = graphql`
fragment name on JsonNode {
name
level
}
`
export const pageQuery = graphql`
query {
one {
...name <- works
}
two {
...name <- works
}
}
`
Это требует небольшой настройки, но, возможно, оно того стоит, если вы заранее знаете свой тип данных и вам нужно многократно использовать фрагменты.