Gatsby.js的GraphQL查询回调 [英] GraphQL query callbacks for Gatsby.js

查看:61
本文介绍了Gatsby.js的GraphQL查询回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In the Contentful CMS, I have two different content-types: BigCaseStudy and BigCaseStudySection. To get this content to appear in my Gatsby 2.x site, my thinking was:

  1. 执行查询1,该查询获取我要显示的所有BigCaseStudy字段,并且还包含内容的ID字段作为元数据.
  2. 从查询1中获取ID,使其与查询2中的内容引用字段(包含ID)相匹配
  3. 执行查询2,返回所有匹配的BigCaseStudySection字段
  1. Do query 1, which gets all the BigCaseStudy fields I want to display, and also contains the content's ID field as metadata.
  2. Take that ID from query 1, match to a Contentful reference field (which contains an ID) in query 2
  3. Do query 2, return all matching BigCaseStudySection fields

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

The end goal would be to display the original BigCaseStudy with all of the BigCaseStudySection (usually numbering between 3-5 of them). You can look at my queries to see the fields, there are bunch.

我认为GraphQL变量和查询的某种组合会使我到达那里(可能是突变)?目前尚不清楚,我还没有看到查询一组东西然后使用响应进行另一个调用的任何复杂示例,例如链式承诺或JS中的async/await.

I think some combination of GraphQL variables and queries would get me there (maybe a mutation)? It's not clear and I haven't seen any complex examples of querying one set of stuff and then using the response to make another call, like chained promises or async/await in JS.

关于正确构造的任何想法吗?

Any ideas on the right construction?

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
}

推荐答案

简短的答案:您不使用GraphQL进行回调.您只需执行一次查询就可以一次获得所有所需的东西.

Short answer: you don't do callbacks with GraphQL. You do one query that gets everything you need all at once.

更长的答案:我不得不重新构造gatsby-node.js文件如何获取Contentful内容,然后对其进行过滤.在Gatsby中,您希望在gatsby-node.js中设置查询以从数据源获取所有内容,因为它是静态站点生成器.它的体系结构将所有数据引入,然后进行相应的解析.

Longer answer: I had to reconstruct how the gatsby-node.js file fetched Contentful content and then filtered through it. In Gatsby, you want to set up the queries in gatsby-node.js to go fetch everything from your data source because it's a static site generator. It's architecture brings all that data in and then parses it out accordingly.

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

The GraphQL query from my original question was fine. I changed .then() of the promise to use .filter() on my results, comparing the relationship field of the child nodes to the id of the parent nodes.

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参数中.

Once you've set this up, the createPage part of Gatsby Node API sends the parent and all of it's nodes over to the component param you set.

在组件内部,我现在可以对所有子节点进行GraphQL查询.现在,这返回了我想要的内容,并符合GraphQL发出一个请求而不是像我尝试的那样发出多个请求的想法.唯一棘手的部分是,您必须在组件的渲染部分中使用.map()来循环遍历从Contentful发回的所有子节点.

Inside of my component, I can now make a GraphQL query for all children nodes. That now returns what I want and conforms to the idea that GraphQL makes one request instead of multiple like I was trying to do. The only tricky part is that you have to use .map() in the render part of the component to loop over all the child nodes sent back from 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重新安排了我处理此问题的方式.

H/t: thanks to @taylor-krusen for rearranging how I was approaching this problem.

这篇关于Gatsby.js的GraphQL查询回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆