Search code examples
reactjsgraphqlgatsbycontentfulstatic-site

Ordering posts in Contentful by date with different Content types


This is my first project using GatsbyJS and Contentful. Right now I have several posts on the site with different Content Models. For the sake of simplicity let's say I have some Content types that are Photos and others that are Video Embeds. I am using GraphQL to fetch the posts...

I have different components for each type.

Photos (also called the PhotoSection)

const data = useStaticQuery(graphql`
    query {
      allContentfulImage(sort: { fields: date, order: DESC }) {
        edges {
          node {
            image {
              description
              file {
                url
              }
            }
          }
        }
      }
    }
  `)

Video Embeds (also called the Film Section)

const data = useStaticQuery(graphql`
    query {
      allContentfulVideoEmbeds(sort: { fields: date, order: DESC }) {
        edges {
          node {
            embedURL
          }
        }
      }
    }
  `)

I then compile all of the components in another component called Blog

const Blog = () => {
  return (
    <div>
      <AudioSection />
      <FilmSection />
      <PhotoSection />
    </div>
  )
}

export default Blog

The end product is that the posts are in descending order by date BUT they are also organized by their section / Content type. If you follow the codeblock the order is AudioSection -> FilmSection -> PhotoSection. I want them to be ordered by Date (latest first) regardless of the Content type.

Hopefully this makes sense. Not really sure what to do here?

Thank you in advance

Edit... this is post attempting what was suggested. i cut out some of the bulkier sections and but left the PhotoComponent as an example

const BlogTwo = () => {
  const data = useStaticQuery(graphql`
    query {
      music: { bla bla bla }
      videos: { bla bla bla }
      images: allContentfulImage(sort: { fields: date, order: DESC }) {
        nodes {
          type: __typename
          image {
            description
            file {
              url
            }
          }
        }
      }
  `)

  const dataForDisplay = [data.music, data.images, data.videos].sort(
    (a, b) => b.date - a.date
  )

  const componentTypeMap = {
    ContentfulMusicAndArt: MusicComponent,
    ContentfulImage: PhotoComponent,
    ContentfulVideoEmbeds: FilmComponent,
  }

  const MusicComponent = () => {
    return (
      <div>
        bla bla bla
      </div>
    )
  }

  const PhotoComponent = () => {
    return (
      <div className={`${blogStyles.blogDiv}`}>
        <div className={`${blogStyles.blogPost} ${blogStyles.image}`}>
          <img
            src={data.images.nodes.image.file.url}
            alt={data.images.nodes.image.description}
          />
        </div>
      </div>
    )
  }

  const FilmComponent = () => {
    return (
      <div>
        bla bla bla
      </div>
    )
  }

  return (
    <div>
      {dataForDisplay.map(({ type, props }) =>
        React.createElement(componentTypeMap[type], props)
      )}
    </div>
  )
}
export default BlogTwo


Solution

  • Instead of wrapping each content type in a WhateverSection component, combine all three arrays of content into a single array, sort it, and then loop through the combined array rendering the relevant component for each entry.

    To make this more sensible, I'm going to refactor your use of static queries by hoisting them up into the blog component, combining them, and adding GraphQL aliases to make retrieving the data less verbose.

    const Blog = () => {
      const data = useStaticQuery(graphql`
        query {
          images: allContentfulImage(sort: { fields: date, order: DESC }) {
            nodes {
              type: __typename
              date
              image {
                description
                file {
                  url
                }
              }
            }
          }
    
          videos: allContentfulVideoEmbeds(sort: { fields: date, order: DESC }) {
            nodes {
              type: __typename
              date
              embedURL
            }
          }
        }
      `)
    
      const dataForDisplay = [...data.images.nodes, ...data.videos.nodes].sort((a, b) => b.date - a.date)
    
      return (
        <div>
          {dataForDisplay.map(({ type, ...props }) => 
            React.createElement(componentTypeMap[type], props)
          )}
        </div>
      )
    }
    
    export default Blog
    
    const componentTypeMap = {
      ContentfulImage: PhotoComponent,
      ContentfulVideoEmbed: FilmComponent,
    }