reactjsgraphqlgatsbycontentfulcontentful-api

I am trying to declare a variable to match Contentful posts that match true on a particular boolean field


I have built a blog with gatsby and Contentful and have a boolean field in the contentful posts to select if a particular post is "featured" or not. How can I declare the variable featuredPost to match posts which have the featured boolean set as true. PLEASE NOTE; in the code below in the declaration I have put ???????? to highlight what/where my question refers to. I would greatly appreciate your advice:

...

class BlogIndex extends React.Component {
  render() {
    const { data } = this.props
    const siteTitle = data.site.siteMetadata?.title || `Title`
    const posts = data.allContentfulPost.edges
    const featuredPost = ???????????

    return (
      <Layout location={this.props.location}>
        <SEO title="FieldPro Blog" keywords={[]} />
        <Header />
        <FeaturedPost>
          {featuredPost.map(({ node }) => {
            const title = node.title || node.slug
            return (
              <div key={node.slug}>
                <FeaturedImage>
                  <Link style={{ boxShadow: `none` }} to={node.slug}>
                    <Img className="Image" fluid={node.image.fluid} />
                  </Link>
                </FeaturedImage>
                <FeaturedText>
                  <FeaturedTitle>
                    <Link style={{ boxShadow: `none` }} to={node.slug}>
                      {title}
                    </Link>
                  </FeaturedTitle>
                  <Labels>
                    <Caption>{/* node.tags */}</Caption>
                  </Labels>
                </FeaturedText>
              </div>
            )
          })}
        </FeaturedPost>
        <PostGroup>
          {posts.map(({ node }) => {
            const title = node.title || node.slug
            return (
              <Post key={node.slug}>
                <PostImage>
                  <Link style={{ boxShadow: `none` }} to={node.slug}>
                    <Img className="Image" fluid={node.image.fluid} />
                  </Link>
                </PostImage>
                <PostText>
                  <Title>
                    <Link style={{ boxShadow: `none` }} to={node.slug}>
                      {title}
                    </Link>
                  </Title>
                  <Labels>
                    <Caption>{/* node.tags */}</Caption>
                  </Labels>
                </PostText>
              </Post>
            )
          })}
        </PostGroup>
      </Layout>
    )
  }
}

export default BlogIndex

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allContentfulPost(sort: { fields: createdAt, order: DESC }) {
      edges {
        node {
          title
          slug
          featured
          image {
            fluid {
              ...GatsbyContentfulFluid
            }
          }
        }
      }
    }
  }
`
...


Solution

  • You can't declare it outside the loop since it's a dependant variable of each element of the loop.

    One built-in option you can do is to filter your GraphQL query on the fly to get only the featured posts. You can create an alias to get both types of posts:

    export const pageQuery = graphql`
      query {
        site {
          siteMetadata {
            title
          }
        }
        featuredPosts: allContentfulPost(filter: { featured: { eq: "true" } }, sort: { fields: createdAt, order: DESC }) {
          edges {
            node {
              title
              slug
              featured
              image {
                fluid {
                  ...GatsbyContentfulFluid
                }
              }
            }
          }
        }
      }
    `
    

    Note: you will need to add the other query for the non-featured posts.

    In this case, your data will be in props.data.featuredPosts.

    The other option, as I said, is to get the featured property of each element while looping through them:

    <FeaturedPost>
      {featuredPost.map(({ node }) => {
        const title = node.title || node.slug;
        if(node.featured){
        return (
          <div key={node.slug}>
            <FeaturedImage>
              <Link style={{ boxShadow: `none` }} to={node.slug}>
                <Img className="Image" fluid={node.image.fluid} />
              </Link>
            </FeaturedImage>
            <FeaturedText>
              <FeaturedTitle>
                <Link style={{ boxShadow: `none` }} to={node.slug}>
                  {title}
                </Link>
              </FeaturedTitle>
              <Labels>
                <Caption>{/* node.tags */}</Caption>
              </Labels>
            </FeaturedText>
          </div>
        )
        }
      })}
    </FeaturedPost>
    

    Your property is inside node.featured and may differ for each element so must be treated inside the loop.