gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.24k stars 10.32k forks source link

Pulling Contentful data down and using gatsby-image for optimatization #5596

Closed DanStockham closed 6 years ago

DanStockham commented 6 years ago

Summary

I've been looking at some ways to optimize the site I am building and came across the plugin gatsby-image

It looks handy however, I'm little stumped as to how to implement it. I have two components that I want to use gatsby-image on: Upcoming event list, and Event Detail template.

The event detail template is pulling data down from Contentful to populate the detail page. This is where I am getting stumped. The way I understand graphql requires you to be specific in your query. How can I both do a query for my contentful data and do the image optimization? This is the same case for the upcoming events, except the query is done on the layout.

Relevant information

EventDetail.js

import React, { Component } from 'react'
import Link from 'gatsby-link'
import Img from 'gatsby-image'

export default class EventDetail extends Component {
  render() {
    const data = this.props.data.contentfulShow;
    const convertDate = new Date(data.date)

    return (
      <div className="content-box">
        <div className="event-detail" >
          {data.image ? <img className="event-detail-image" src={data.image.file.url} alt=""/> : null }
          <div className="event-detail__content">
            <h2 className="event-detail-name">{data.name.toUpperCase()}</h2>
            <h3 className="event-detail-date">{`${convertDate.toLocaleDateString({ weekday: 'short', month:'short', day:'numeric'}).toUpperCase()} ${convertDate.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'})}`}</h3>
            <h3 className="event-detail-coverprice" >{!data.coverPrice ? `No Cover` : `$${data.coverPrice.toFixed(2)}`}</h3>
            <div dangerouslySetInnerHTML={{__html: data.description.childMarkdownRemark.html}} />
            <Link to='/'>Back to Upcoming Events</Link>
          </div>
        </div>
      </div>
    )
  }
}

export const eventDetailQuery = graphql`
  query eventDetailQuery($slug: String!) {
      contentfulShow (slug: {eq: $slug}) {
        slug
        name
        date
        coverPrice
        description {
          id
          childMarkdownRemark {
            id
            html
          }
        }
        image {
          file {
            url
          }
        }
    }
  }

`

UpcomingEvents.js

import React, { Component, PropTypes } from 'react';
import Link from 'gatsby-link'
import Pagination from './Pagination'
import Event from './Event'

export default class UpcomingEvents extends Component { // eslint-disable-line react/prefer-stateless-function
  render() {
    const { group, index, first, last, pageCount, additionalContext } = this.props.pathContext;
    const showCount = additionalContext.showCount;
    const paginateControls = { index, first, last, showCount, pageCount};

    const events = group.map((node) => {
      const show = node.node;
      return <Event key={show.id} 
                    showName={show.name} 
                    showDate={show.date} 
                    coverPrice={show.coverPrice}
                    image={show.image ? show.image.file.url : null}
                    slug={show.slug} />
    });

    return (
      <div className="content-box">
        <h3 className="content-box__header-tab">UPCOMING EVENTS</h3>
        <div className="c-eventsList">
          <Pagination paginateControls={paginateControls}/>
          { events }
          <Pagination paginateControls={paginateControls}/>
        </div>
      </div>
    );
  }
}

Layout.js

import React from 'react'
import PropTypes from 'prop-types'
import Link from 'gatsby-link'
import Helmet from 'react-helmet'

import lobsterFont from "typeface-lobster"

import Navigation from '../components/Navigation'
import SocialMedia from '../components/SocialMedia'
import NextEvent from '../components/NextEvent'
import AddressBlock from '../templates/AddressBlock'
import Footer from '../components/Footer'

import 'bootstrap/dist/css/bootstrap.css';
import '../styles/styles.min.css';

const Header = () => (
  <div
    className="header"
  >
    <div
      style={{
        margin: '0 auto',
        maxWidth: 960,
        padding: '1.45rem 1.0875rem',
      }}
    >
      <h1 className="header__logo">
        <Link
          to="/"
          style={{
            color: 'white',
            textDecoration: 'none',
          }}
        >
          Tootle's
        </Link>
      </h1>
    </div>
  <Navigation />
  </div>
)

const TemplateWrapper = ({data, children}) => {
  const nextShow = data.allContentfulShow.edges[0];
  return (
    <div>
      <div className="c-background-image"></div>
      <div className="c-background-overlay"></div>
      <div className="wrapper">
        <Header />
        <div className="c-layout">
          <Helmet
            title="Tootle's Pumpkin Inn"
            meta={[
              { name: 'description', content: 'Sample' },
              { name: 'keywords', content: 'sample, something' },
            ]}
            >
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
            <link rel="stylesheet" href="style.css"/>
          </Helmet>

          <div className="c-layout__content">
            <div className="c-layout__main">
              {children()}
            </div>
            <div className="c-layout__aside">
              <SocialMedia />
              <NextEvent nextShow={nextShow}/>
              <AddressBlock/>
            </div>
          </div>
          <Footer/>
        </div>
      </div>
    </div>
  )
}

TemplateWrapper.propTypes = {
  children: PropTypes.func,
}

export default TemplateWrapper

export const nextEventQuery = graphql`
  query nextEventQuery {
    allContentfulShow (
      limit: 1
      sort: { fields: [date], order:DESC }
    ) {
      edges {
        node {
          name
          date
          slug
          coverPrice
          image {
          file {
            url
          }
          }
        }
      }
    }
  }
`
ryanditjia commented 6 years ago

gatsby-source-contentful exposes GraphQL fragments so you can use Contentful Images API easily with gatsby-image. It’s briefly documented here.

Example in your case:

export const nextEventQuery = graphql`
  query nextEventQuery {
    allContentfulShow (
      limit: 1
      sort: { fields: [date], order:DESC }
    ) {
      edges {
        node {
          name
          date
          slug
          coverPrice
          image {
            sizes(maxWidth: 1280) {
              ...GatsbyContentfulSizes
            }
          }
        }
      }
    }
  }
`
const sizes = data.allContentfulShow.edges[0].node.image.sizes
<Img sizes={sizes} />
DanStockham commented 6 years ago

@ryanditjia I kinda get it now. I tried setting up a query similar to what you have on gatsby-node to pull in my paginated shows however, I get an error stating the fragment doesn't exist:

GraphQLError: Unknown fragment "GatsbyContentfulResolutions"

I apologize if the fix for this is known. I'm not very familiar with graphql

KyleAMathews commented 6 years ago

Try moving the query into the page component.

ryanditjia commented 6 years ago

If you’re getting the error inside GraphiQL, that’s because the fragment doesn’t work with it.

Feel free to ask if you still have more questions with GraphQL, it’s a lot of fun once you get the hang of it.

KyleAMathews commented 6 years ago

Due to the high volume of issues, we're closing out older ones without recent activity. Please open a new issue if you need help!