Gatsby: array mapping with gatsby-img

Created on 31 Jan 2019  路  6Comments  路  Source: gatsbyjs/gatsby

Summary

There are 24 images inside 'src/assets/sponsors/'. The goal is to iterate and render in loop with gatsby-image.

With gatsby-source-filesystem is installed, inside gatsby-config.js I have included path:/assets/sponsors. This ensure all (_24_) images get fetched from sponsors directory only.

_gatsby-config.js_

  module.exports = {
...
    {
        resolve: `gatsby-source-filesystem`,
        options: {
        name: `sponsors`,
        path: `${__dirname}/src/assets/sponsors`
        }
    },
    };
...

Querying graphQL from http://localhost:8000/___graphql/...

{
  allFile(filter: { absolutePath: {regex: "/sponsors/"}}){
    edges{
      node{
        childImageSharp{
          fluid{
            src
          }
        }
      }
    }
  }
}

The result went out good. This display lists of all 24 images from /assets/sponsors only.

Relevant information

inside _/template/sponsors.js_...

import React from 'react';
import { graphql } from 'gatsby';
import Img from 'gatsby-image';

const sponsors = ({ data }) => (
  <div>
    {data.map((items, i) => (
       <Img
            key={i}
            fluid={items.node.childImageSharp.fluid}
            alt="all-about-you"
       />
    ))}
  </div>
);

export default sponsors;

export const pageQuery = graphql`
    query imageQuery {
      source: allFile(
        filter: {
                        absolutePath: { regex: "/sponsors/" }
                     }
                     ) {
                         edges {
                             node {
                                  childImageSharp { 
                                       fluid(maxWidth: 600) {
                                           ...GatsbyImageSharpFluid
                                       }
                                   }
                              }
                         }
                  }
           }
`;

... After compiling, there wasn't any indication of warning or error. However, from the browser I am getting...

TypeError: Cannot read property 'map' of undefined
```javascript
4 | import Img from 'gatsby-image';
5 |
6 | const IndexPage = ({ data }) => (

7 |


8 | {data.map((items, i) => (
9 | 10 | key={i}```

struggling to resolve this... need help, what am I missing here?

Environment

Environment
System: Custom Build
OS: Win10 Pro
CPU: AMD FX-8320 Eight-Core Processors
Shell: zsh 5.1.1
Binaries:
Node: 10.12.0
yarn: 1.12.3
Browsers:
Chrome: 71.0.3578.98
Firefox: 66.0b2

npmPackages:
"babel-plugin-styled-components": "^1.10.0",
"gatsby": "^2.0.76",
"gatsby-image": "^2.0.20",
"gatsby-plugin-manifest": "^2.0.9",
"gatsby-plugin-offline": "^2.0.16",
"gatsby-plugin-react-helmet": "^3.0.2",
"gatsby-plugin-resolve-src": "^2.0.0-beta.1",
"gatsby-plugin-sharp": "^2.0.14",
"gatsby-plugin-styled-components": "^3.0.4",
"gatsby-remark-images": "^3.0.1",
"gatsby-remark-relative-images": "^0.2.1",
"gatsby-source-filesystem": "^2.0.8",
"gatsby-transformer-remark": "^2.2.0",
"gatsby-transformer-sharp": "^2.1.8",
"polished": "^2.3.3",
"prop-types": "^15.6.2",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-helmet": "^5.2.0",
"react-pose": "^4.0.4",
"rimraf": "^2.6.2",
"styled-components": "^4.1.4-alpha.5"

awaiting author response question or discussion

Most helpful comment

@aprather51 i've picked up on your issue and based on the information at hand, and i have a solution for you, i've figured out what is the problem and at the end of my comment you'll understand why. Going to break down into smaller pieces my comment for a better understanding.

  • Started by creating a new website based on the default starter
  • Copied some old wallpapers i had laying around in a folder and i've recreated the folder structure to match yours as you can see bellow:
    list_of_images

  • Adjusted my gatsby-config.js file to match yours, as you can see bellow:

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve:`gatsby-source-filesystem`,
      options:{
        name:`sponsors`,
        path: `${__dirname}/src/assets/sponsors`
      }
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}
  • Created the templates folder with Emojicons.js inside with the code you provided and imported it to the index.js file.
  • Issued gatsby develop and opening http://localhost:8000, confirmed your issue.
  • Adjusted of the template to this:
import React from 'react'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'

const sponsors = ({ data }) => {
  console.log('====================================')
  console.log(`data value:${JSON.stringify(data, null, 2)}`)
  console.log('====================================')
  return <div>soon</div>
}

export default sponsors

export const pageQuery = graphql`
  query imageQuery {
    source: allFile(filter: { absolutePath: { regex: "/sponsors/" } }) {
      edges {
        node {
          childImageSharp {
            fluid(maxWidth: 600) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`
  • Re-issued gatsby develop, opening http://localhost:8000 and the devtools is presented me with this:
    undef_template
  • To avoid this behaviour i've created new component Component_Emojicons.js under the components folder with the following code:
import React from 'react'
import { StaticQuery, graphql } from 'gatsby'
import Img from 'gatsby-image'

const Sponsors_Component = () => (
  <StaticQuery
    query={graphql`
      query allimgQuery {
        source: allFile(filter: { absolutePath: { regex: "/sponsors/" } }) {
          edges {
            node {
              childImageSharp {
                fluid(maxWidth: 2000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    `}
    render={data => (
        <div>
        {data.source.edges.map(({ node }, i) => (
        <Img key={i} fluid={node.childImageSharp.fluid} />
      ))}
    </div>
    )}
  />
)
export default Sponsors_Component

  • Replaced the import on the index and now i'm presented with this:
    images_showing

Now, with that out of the way. Based on what you described and posted in the codesandox example. What you're doing is at the lack of a better word a anti-pattern in Gatsby's terms and that's why that behaviour happens.

Templates in Gatsby are React components that will be served under a specific endpoint, turning them into pages.
Those pages are constructed programmatically, more on that here.

You are using a page query in a component that is then imported to a page. From a React/graphql point of view its valid, but when the page renders you're presented with undefined like you're issue describes and my image above illustrates.
The approach you need to take is the one i took and also the one used in the layout.js file.
More on the difference between page queries and static here,
Hope i could shed some insights on your issue. Feel free to provide feedback

All 6 comments

@aprather51 You need to map over data.allFile.edges

With GraphQL, the format of the data corresponds directly to the query

Mapped data.allFile.edges following...

  <div>
    {data.allFile.edges.map((items, i) => (
        <Img key={i} fluid={items.node.childImageSharp.fluid} />
    ))}
</div>

And I still get _TypeError: Cannot read property 'allFile' of undefined._. And also with console.log(data) the browser console show this as undefined. I am assuming it does not detect any data?

@aprather51 Looks like you've aliased allFile to source in the query

So you will need to access it as data.source.edges

"_So you will need to access it as data.source.edges_"
I did that, and it didn't work at all. Apparent, data is being undefined after confirming it from console log.

here's embed link, with close exact copy of syntax...
https://codesandbox.io/embed/oxp4x0mpoz

Hope this this could help give you some insight on my issues.

@aprather51 i've picked up on your issue and based on the information at hand, and i have a solution for you, i've figured out what is the problem and at the end of my comment you'll understand why. Going to break down into smaller pieces my comment for a better understanding.

  • Started by creating a new website based on the default starter
  • Copied some old wallpapers i had laying around in a folder and i've recreated the folder structure to match yours as you can see bellow:
    list_of_images

  • Adjusted my gatsby-config.js file to match yours, as you can see bellow:

module.exports = {
  siteMetadata: {
    title: `Gatsby Default Starter`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve:`gatsby-source-filesystem`,
      options:{
        name:`sponsors`,
        path: `${__dirname}/src/assets/sponsors`
      }
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}
  • Created the templates folder with Emojicons.js inside with the code you provided and imported it to the index.js file.
  • Issued gatsby develop and opening http://localhost:8000, confirmed your issue.
  • Adjusted of the template to this:
import React from 'react'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'

const sponsors = ({ data }) => {
  console.log('====================================')
  console.log(`data value:${JSON.stringify(data, null, 2)}`)
  console.log('====================================')
  return <div>soon</div>
}

export default sponsors

export const pageQuery = graphql`
  query imageQuery {
    source: allFile(filter: { absolutePath: { regex: "/sponsors/" } }) {
      edges {
        node {
          childImageSharp {
            fluid(maxWidth: 600) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`
  • Re-issued gatsby develop, opening http://localhost:8000 and the devtools is presented me with this:
    undef_template
  • To avoid this behaviour i've created new component Component_Emojicons.js under the components folder with the following code:
import React from 'react'
import { StaticQuery, graphql } from 'gatsby'
import Img from 'gatsby-image'

const Sponsors_Component = () => (
  <StaticQuery
    query={graphql`
      query allimgQuery {
        source: allFile(filter: { absolutePath: { regex: "/sponsors/" } }) {
          edges {
            node {
              childImageSharp {
                fluid(maxWidth: 2000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    `}
    render={data => (
        <div>
        {data.source.edges.map(({ node }, i) => (
        <Img key={i} fluid={node.childImageSharp.fluid} />
      ))}
    </div>
    )}
  />
)
export default Sponsors_Component

  • Replaced the import on the index and now i'm presented with this:
    images_showing

Now, with that out of the way. Based on what you described and posted in the codesandox example. What you're doing is at the lack of a better word a anti-pattern in Gatsby's terms and that's why that behaviour happens.

Templates in Gatsby are React components that will be served under a specific endpoint, turning them into pages.
Those pages are constructed programmatically, more on that here.

You are using a page query in a component that is then imported to a page. From a React/graphql point of view its valid, but when the page renders you're presented with undefined like you're issue describes and my image above illustrates.
The approach you need to take is the one i took and also the one used in the layout.js file.
More on the difference between page queries and static here,
Hope i could shed some insights on your issue. Feel free to provide feedback

I see now, this all make sense now. Finally, that did insights on my long issues with Gatsby and queries. Thank you @jonniebigodes and @sidharthachatterjee surrender time to answer my question.

Was this page helpful?
0 / 5 - 0 ratings