GraphQL查询回调Gatsby.js(GraphQL query callbacks for G

2019-09-30 21:45发布

在Contentful CMS,我有两个不同的内容类型: BigCaseStudyBigCaseStudySection 。 要获得这些内容出现在我的盖茨比2.x的网站,我的想法是:

  1. 做查询1,它可以获取所有的BigCaseStudy领域我想显示,并且还包含内容的ID字段作为元数据。
  2. 采取ID从查询1,匹配到Contentful基准场(其中包含一个ID )查询2
  3. 请查询2,返回所有匹配BigCaseStudySection领域

最终目标将是显示原始BigCaseStudy与所有的BigCaseStudySection (通常是他们的3-5编号)。 你可以看一下我的查询看到的领域,也有一堆。

我认为GraphQL变量和查询的一些组合将让我有(可能是突变)? 目前尚不清楚,我还没有看到查询一套东西,然后使用响应拨打另一个电话,像链式承诺或异步的任何复杂的例子/等待在JS。

右边建设任何想法?

bigcasestudy.js与GraphQL组件查询:

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
}

Answer 1:

我就遇到了这个挑战,也没找到一个很好的解决方案来实现这一目标(虽然我没有使用Contentful),但我没有过去,它的工作,并认为我可以提供帮助。 你需要改变你的思考了一下。

基本上,GraphQL是不是真的意味着查询您需要运行另一个查询的数据。 它更多工具的“问你需要什么样的。 GraphQL要为您所需要东西运行的单个查询

您需要为您的查询参数实际上来自您的gatsby-node.js文件。 具体而言,上下文属性createPages()节点的API,使得盖茨比可提供)。

那是足以让你指出正确的方向? 如果您需要多一点手,那么有两件事情我需要知道:
1.多一点背景下身边你要完成的任务。 什么是你要提供给最终用户的具体数据?
2.你什么gatsby-node.js文件的样子。



Answer 2:

简短的回答:你不GraphQL做回调。 你做一个查询,让你需要的所有的一切一次。

更长的答案:我不得不重建如何gatsby-node.js文件中取得Contentful内容,然后通过它过滤。 在盖茨比,你要设置的查询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
}

一旦你设置这件事时, createPage盖茨比节点API的一部分向父母和它的所有节点上的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:感谢@泰勒的鼓手Krusen重新安排我是如何处理这个问题。



文章来源: GraphQL query callbacks for Gatsby.js