Gatsby.js的GraphQL查询回调

问题描述 投票:1回答:2

在Contentful CMS中,我有两种不同的内容类型:BigCaseStudyBigCaseStudySection。为了让这些内容出现在我的Gatsby 2.x网站上,我的想法是:

  1. 查询1,它获取我想要显示的所有BigCaseStudy字段,还包含内容的ID字段作为元数据。
  2. 从查询1获取ID,匹配查询2中的Contentful引用字段(包含ID
  3. 执行查询2,返回所有匹配的BigCaseStudySection字段

最终目标是显示所有BigCaseStudy的原始BigCaseStudySection(通常在3-5个之间编号)。您可以查看我的查询以查看字段,有一堆。

我认为GraphQL变量和查询的某些组合会让我(可能是一个变异)?目前尚不清楚,我还没有看到任何查询一组内容然后使用响应进行另一次调用的复杂示例,如JS链接的promises或async / await。

关于正确建设的任何想法?

带有GraphQL查询的bigcasestudy.js组件:

import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import { graphql } from 'gatsby'
import Img from 'gatsby-image'

import Layout from '../Layout/layout'

/** 
 * Hero Section
 */ 
const HeroContainer = styled.header`
  align-items: center;
  background-image: url(${ props => props.bgImgSrc });
  background-position: center center;
  background-size: cover;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: calc(100vh - 128px);
`
const HeroTitle = styled.h1`
  color: #fff;
  font-size: 70px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 15px;
`
const HeroSubtitle = styled.h2`
  color: #fff;
  font-size: 24px;
  font-weight: 300;
  letter-spacing: 5px;
  text-transform: uppercase;
`
/** 
 * Intro Section
 */ 
const IntroBG = styled.section`
  background-color: ${ props => props.theme.lightGray };
  padding: 50px 0;
`
const IntroContainer = styled.div`
  padding: 25px;
  margin: 0 auto;
  max-width: ${ props => props.theme.sm };

  @media (min-width: ${ props => props.theme.sm }) {
    padding: 50px 0;
  }
`
const IntroTitle = styled.h2`
  font-size: 50px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 45px;
  text-align: center;
`
const IntroText = styled.p`
  font-size: 22px;
  line-spacing: 4;
  text-align: center;
`

const IntroButton = styled.a`
  background-color: #fff;
  color: ${ props => props.theme.darkGray };
  border: 1px solid ${ props => props.theme.mediumGray };
  border-radius: 25px;
  display: block;
  font-size: 16px;
  letter-spacing: 5px;
  margin: 30px auto;
  padding: 15px 45px;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  width: 300px;
`

// BigCaseStudy Component
class BigCaseStudy extends React.Component {
  render() {
    // Setup destructured references to each Contentful object passed in through the GraphQL call
    const { caseStudyTitle } = this.props.data.contentfulBigCaseStudy
    const { caseStudySubtitle } = this.props.data.contentfulBigCaseStudy
    const { caseStudyIntroTitle } = this.props.data.contentfulBigCaseStudy
    const { caseStudyIntro } = this.props.data.contentfulBigCaseStudy.caseStudyIntro
    const { caseStudyLink } = this.props.data.contentfulBigCaseStudy

    console.log(this)

    return (
      <Layout>
        <HeroContainer 
          bgImgSrc={ this.props.data.contentfulBigCaseStudy.caseStudyHero.fixed.src }>
          <HeroTitle>{ caseStudyTitle }</HeroTitle>
          <HeroSubtitle>{ caseStudySubtitle }</HeroSubtitle>
        </HeroContainer>
        <IntroBG>
          <IntroContainer>
            <IntroTitle>{ caseStudyIntroTitle }</IntroTitle>
            <IntroText>{ caseStudyIntro }</IntroText>
          </IntroContainer>
          <IntroButton href={ caseStudyLink } target="_blank" rel="noopener noreferrer">
            Visit the site >
          </IntroButton>
        </IntroBG>
      </Layout>
    )
  }
}

// Confirm data coming out of contentful call is an object
BigCaseStudy.propTypes = {
  data: PropTypes.object.isRequired
}

// Export component
export default BigCaseStudy

// Do call for the page data
// This needs to mirror how you've set up the dynamic createPage function data in gatsby-node.js
export const BigCaseStudyQuery = graphql`
  query BigCaseStudyQuery {
    contentfulBigCaseStudy {
      id
      caseStudyTitle
      caseStudySubtitle
      caseStudyIntroTitle
      caseStudyIntro {
        caseStudyIntro
      }
      caseStudyLink
      caseStudyHero {
        fixed {
          width
          height
          src
          srcSet
        }                  
      }
    },
    contentfulBigCaseStudySection (id: $postId) {
      title
      order
      images {
        fixed {
          width
          height
          src
          srcSet
        }
      }
      bigCaseStudyReference {
        id
      }
      body {
        body
      }
      stats {
        stat1 {
          word
          number
        }
        stat2 {
          word
          number
        }
        stat3 {
          word
          number
        }
        stat4 {
          word
          number
        } 
      }
      id
    }
  }
`

gatsby-node.js文件:

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * ######################################################
 * BIG CASE STUDY BACKEND CODE
 * ######################################################
 * 
 * We are using the .createPages part of the Gatsby Node API: https://next.gatsbyjs.org/docs/node-apis/#createPages 
 * What this does is dynamically create pages (surprise) based on the data you feed into it
 * 
 * Feed the contentful API call into the promise
 * Here I'm calling BigCaseStudy, which is a custom content type set up in contentful
 * This is briefly explained over here: https://www.gatsbyjs.org/packages/gatsby-source-contentful/
 * 
 * Also, note the caseStudyIntro field is long text `markdown`
 * Gatsby returns the long text field as an object
 * Calling it's name inside of the object returns the HTML
 * Read more here: https://github.com/gatsbyjs/gatsby/issues/3205
 */

// Set Gatsby path up to be used by .createPages
const path = require('path')

// Using Node's module export, Gatsby adds in a createPages factory 
exports.createPages = ({ graphql, actions }) => {

  // We setup the createPage function takes the data from the actions object
  const { createPage } = actions

  // Setup a promise to build pages from contentful data model for bigCaseStudies
  return new Promise((resolve, reject) => {

    // Setup destination component for the data
    const bigCaseStudyComponent = path.resolve('src/components/BigCaseStudy/bigcasestudy.js')

    resolve(
      graphql(`
        {
          allContentfulBigCaseStudy {
            edges {
              node { 
                id
                caseStudySlug
                caseStudyTitle
                caseStudySubtitle
                caseStudyIntroTitle
                caseStudyIntro {
                  caseStudyIntro
                }
                caseStudyLink
                caseStudyHero {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }                  
                }
              }
            }
          }
          allContentfulBigCaseStudySection {
            edges {
              node {
                title
                order
                images {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }
                }
                bigCaseStudyReference {
                  id
                }
                body {
                  body
                }
                stats {
                  stat1 {
                    word
                    number
                  }
                  stat2 {
                    word
                    number
                  }
                  stat3 {
                    word
                    number
                  }
                  stat4 {
                    word
                    number
                  } 
                }
                id
              }
            }
          }
        }
      `).then((result) => {

        // Now we loop over however many caseStudies Contentful sent back
        result.data.allContentfulBigCaseStudy.edges.forEach((caseStudy) => {
          const caseStudySections = result.data.allContentfulBigCaseStudySection.edges

          let caseStudySectionMatches = caseStudySections.filter( 
            caseStudySection => caseStudySection.bigCaseStudyReference.id === caseStudy.id 
          )

          createPage ({
            path: `/work/${caseStudy.node.caseStudySlug}`,
            component: bigCaseStudyComponent,
            context: {
              id: caseStudy.node.id,
              slug: caseStudy.node.caseStudySlug,
              title: caseStudy.node.caseStudyTitle,
              subtitle: caseStudy.node.caseStudySubtitle,
              hero: caseStudy.node.caseStudyHero,
              introTitle: caseStudy.node.caseStudyIntroTitle,
              intro: caseStudy.node.caseStudyIntro.caseStudyIntro,
              link: caseStudy.node.caseStudyLink,
              caseStudySection: caseStudySectionMatches.node
            }
          })

        })
      })

      // This is the error handling for the calls
      .catch((errors) => {
        console.log(errors)
        reject(errors)
      })

    ) // close resolve handler
  }) // close promise
}
javascript graphql styled-components gatsby contentful
2个回答
1
投票

我也遇到了这个挑战,并没有找到一个很好的解决方案来实现这一目标(虽然我没有使用Contentful),但我确实已经完成了它并认为我可以提供帮助。你需要改变一下你的想法。

基本上,GraphQL实际上不是要查询运行另一个查询所需的数据。它更像是一种“问你需要什么”的工具。 GraphQL希望针对您的需求运行单个查询。

查询所需的参数实际上来自您的gatsby-node.js文件。具体来说,createPages()的上下文属性(gatsby提供的Node API)。

这足以让你指向正确的方向吗?如果你需要多一点手,那么我需要知道两件事: 1.关于你想要完成的事情的更多背景。您希望最终用户获得哪些具体数据? 2.你的gatsby-node.js文件是什么样的。


1
投票

简短的回答:你不用GraphQL做回调。您只需一个查询即可获得所需的所有内容。

更长的答案:我必须重建gatsby-node.js文件如何获取Contentful内容然后通过它进行过滤。在Gatsby中,您希望在gatsby-node.js中设置查询以从数据源中获取所有内容,因为它是静态站点生成器。它的架构将所有数据都包含在内,然后相应地解析出来。

我原来问题的GraphQL查询很好。我更改了.then()的承诺,在我的结果上使用.filter(),将子节点的关系字段与父节点的id进行比较。

gatsby-node.js

// Set Gatsby path up to be used by .createPages
const path = require('path')

// Using Node's module export, Gatsby adds in a createPages factory 
exports.createPages = ({ graphql, actions }) => {

  // We setup the createPage function takes the data from the actions object
  const { createPage } = actions

  // Setup a promise to build pages from contentful data model for bigCaseStudies
  return new Promise((resolve, reject) => {

    // Setup destination component for the data
    const bigCaseStudyComponent = path.resolve('src/components/BigCaseStudy/bigcasestudy.js')

    resolve(
      graphql(`
        {
          allContentfulBigCaseStudy {
            edges {
              node { 
                id
                caseStudySlug
                caseStudyTitle
                caseStudySubtitle
                caseStudyIntroTitle
                caseStudyIntro {
                  caseStudyIntro
                }
                caseStudyLink
                caseStudyHero {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }                  
                }
              }
            }
          }
          allContentfulBigCaseStudySection {
            edges {
              node {
                title
                order
                images {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }
                }
                bigCaseStudyReference {
                  id
                }
                body {
                  body
                }
                stats {
                  stat1 {
                    word
                    number
                  }
                  stat2 {
                    word
                    number
                  }
                  stat3 {
                    word
                    number
                  }
                  stat4 {
                    word
                    number
                  } 
                }
                id
              }
            }
          }
        }
      `).then((result) => {

        // Now we loop over however many caseStudies Contentful sent back
        result.data.allContentfulBigCaseStudy.edges.forEach((caseStudy) => {
          let matchedCaseStudySections = result.data.allContentfulBigCaseStudySection.edges.filter(
            caseStudySection => 
              caseStudySection.node.bigCaseStudyReference.id === caseStudy.node.id 
          )

          createPage ({
            path: `/work/${caseStudy.node.caseStudySlug}`,
            component: bigCaseStudyComponent,
            context: {
              id: caseStudy.node.id,
              slug: caseStudy.node.caseStudySlug,
              title: caseStudy.node.caseStudyTitle,
              subtitle: caseStudy.node.caseStudySubtitle,
              hero: caseStudy.node.caseStudyHero,
              introTitle: caseStudy.node.caseStudyIntroTitle,
              intro: caseStudy.node.caseStudyIntro.caseStudyIntro,
              link: caseStudy.node.caseStudyLink,
              caseStudySection: matchedCaseStudySections.node
            }
          })

        })
      })

      // This is the error handling for the calls
      .catch((errors) => {
        console.log(errors)
        reject(errors)
      })

    ) // close resolve handler
  }) // close promise
}

一旦你设置好了,Gatsby Node API的createPage部分就会将父节点及其所有节点发送到你设置的component param。

在我的组件内部,我现在可以为所有子节点进行GraphQL查询。这现在返回我想要的东西,并符合GraphQL提出一个请求而不是像我试图做的多个请求的想法。唯一棘手的部分是你必须在组件的渲染部分中使用.map()来遍历从Contentful发回的所有子节点。

bigcasestudy.js组件:

import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import { graphql } from 'gatsby'
import Img from 'gatsby-image'

import Layout from '../Layout/layout'

/** 
 * Hero Section
 */ 
const HeroContainer = styled.header`
  align-items: center;
  background-image: url(${ props => props.bgImgSrc });
  background-position: center center;
  background-size: cover;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: calc(100vh - 128px);
`
const HeroTitle = styled.h1`
  color: #fff;
  font-size: 70px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 15px;
`
const HeroSubtitle = styled.h2`
  color: #fff;
  font-size: 24px;
  font-weight: 300;
  letter-spacing: 5px;
  text-transform: uppercase;
`
/** 
 * Intro Section
 */ 
const IntroBG = styled.section`
  background-color: ${ props => props.theme.lightGray };
  padding: 50px 0;
`
const IntroContainer = styled.div`
  padding: 25px;
  margin: 0 auto;
  max-width: ${ props => props.theme.sm };

  @media (min-width: ${ props => props.theme.sm }) {
    padding: 50px 0;
  }
`
const IntroTitle = styled.h2`
  font-size: 50px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 45px;
  text-align: center;
`
const IntroText = styled.p`
  font-size: 22px;
  line-spacing: 4;
  text-align: center;
`

const IntroButton = styled.a`
  background-color: #fff;
  color: ${ props => props.theme.darkGray };
  border: 1px solid ${ props => props.theme.mediumGray };
  border-radius: 25px;
  display: block;
  font-size: 16px;
  letter-spacing: 5px;
  margin: 30px auto;
  padding: 15px 45px;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  width: 300px;
`



// BigCaseStudy Component
class BigCaseStudy extends React.Component {
  render() {

    // Destructure Case Study Intro stuff
    const { 
      caseStudyHero, 
      caseStudyIntro, 
      caseStudyIntroTitle, 
      caseStudyLink, 
      caseStudySubtitle, 
      caseStudyTitle 
    } = this.props.data.contentfulBigCaseStudy

    // Setup references to Case Study Sections, destructure BigCaseStudySection object
    const caseStudySections = this.props.data.allContentfulBigCaseStudySection.edges.map( 
      (currentSection) => {
        return currentSection.node
      }
    )

    // Case Study Section can be in any order, so we need to sort them out
    const caseStudySectionsSorted = caseStudySections.sort( (firstItem, secondItem) => {
      return firstItem.order > secondItem.order ? 1 : -1
    })

    console.log(caseStudySectionsSorted)

    return (
      <Layout>
        <HeroContainer 
          bgImgSrc={ caseStudyHero.fixed.src }>
          <HeroTitle>{ caseStudyTitle }</HeroTitle>
          <HeroSubtitle>{ caseStudySubtitle }</HeroSubtitle>
        </HeroContainer>
        <IntroBG>
          <IntroContainer>
            <IntroTitle>{ caseStudyIntroTitle }</IntroTitle>
            <IntroText>{ caseStudyIntro.caseStudyIntro }</IntroText>
          </IntroContainer>
          <IntroButton href={ caseStudyLink } target="_blank" rel="noopener noreferrer">
            Visit the site >
          </IntroButton>
        </IntroBG>
        {
          caseStudySectionsSorted.map( (caseStudySection, index) => {
            return <IntroTitle key={ index }>{ caseStudySection.title }</IntroTitle>
          })
        }
      </Layout>
    )
  }
}

// Confirm data coming out of contentful call is an object
BigCaseStudy.propTypes = {
  data: PropTypes.object.isRequired
}

// Export component
export default BigCaseStudy

// Do call for the page data
// This needs to mirror how you've set up the dynamic createPage function data in gatsby-node.js
export const BigCaseStudyQuery = graphql`
  query BigCaseStudyQuery {
    contentfulBigCaseStudy {
      id
      caseStudyTitle
      caseStudySubtitle
      caseStudyIntroTitle
      caseStudyIntro {
        caseStudyIntro
      }
      caseStudyLink
      caseStudyHero {
        fixed {
          width
          height
          src
          srcSet
        }                  
      }
    }
    allContentfulBigCaseStudySection {
      edges {
        node {
          title
          order
          images {
            fixed {
              width
              height
              src
              srcSet
            }
          }
          bigCaseStudyReference {
            id
          }
          body {
            body
          }
          stats {
            stat1 {
              word
              number
            }
            stat2 {
              word
              number
            }
            stat3 {
              word
              number
            }
            stat4 {
              word
              number
            } 
          }
          id
        }
      }
    }
  }
`

H / t:感谢@ taylor-krusen重新安排我如何解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.