React Leaflet works during development, fails on build time, noting to the lack of constructor icon

Viewed 237

I use GatsbyJS, and Leaflet to create a map. The only problem is I want to use multiple icons on the map as a marker icon. Everything works during development, fully functional, but when I try "gatsby build", it fails:

Here is the error

                          < Marker
  25 |                       position={[`${genome.coordinate_x}`,`${genome.coordinate_y}`]}
> 26 |                       icon = {new Icon({
     |                               ^
  27 |                         iconUrl: genome.bee_icon.childImageSharp.fluid.src,
  28 |                         iconSize: [60],
  29 |                         iconAnchor: [20, 40]
  WebpackError: TypeError: leaflet__WEBPACK_IMPORTED_MODULE_3__.Icon is not a constructor

Here is the code of the page

import React from "react"
import LayoutNoPadding from "../components/layoutNoPadding"
import { Map, Marker, Popup, TileLayer, Tooltip } from 'react-leaflet'
import {Icon} from 'leaflet'
import {graphql} from 'gatsby'
import Link from 'gatsby-link'
import SEO from '../components/SEO'
import Img from 'gatsby-image'


const IndexPage = ({data}) => {
  const {allStrapiGenomes:{nodes:genomes}} = data

  return (
  <LayoutNoPadding>
    <SEO title="WWB" />
        <Map center={[0, 0]} zoom={2} maxZoom={5} minZoom={2} attributionControl={false} >
                <TileLayer
                url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png"
                 />
                {genomes.map((genome)=> {
                  return (
                    <div className="bee-icons">
                    <Marker
                      position={[`${genome.coordinate_x}`,`${genome.coordinate_y}`]}
                      icon = {new Icon({
                        iconUrl: genome.bee_icon.childImageSharp.fluid.src,
                        iconSize: [60],
                        iconAnchor: [20, 40]
                      })}
                    >
                      <Tooltip>{genome.bee_name}</Tooltip>
                      <Popup>
                        <Img fluid={genome.bee_icon.childImageSharp.fluid} alt={genome.bee_name} />
                        <h1 className="text-xl py-2">{genome.bee_name}</h1>
                        <h1 className="text-lg text-gray-500">{genome.bee_genome}</h1>
                        <p className="text-sm text-gray-700 pb-2">{genome.summary}</p>
                        <Link fade to={`/genomes/${genome.slug}`} className="flex bg-gray-200 py-2 px-4 focus:outline-none text-lg cursor-pointer text-white rounded-full justify-center text-white hover:bg-gray-300 transition-all duration-500 ease-in-out">Read More</Link>
                      </Popup>
                    </Marker>
                </div>
                  )
                })}
        </Map>
  </LayoutNoPadding>
  )
}
export default IndexPage

export const query = graphql`
  {
    allStrapiGenomes {
      nodes {
        bee_icon {
          childImageSharp {
            fluid {
              ...GatsbyImageSharpFluid
            }
          }
        }
        bee_genome
        coordinate_x
        coordinate_y
        summary
        strapiId
        bee_photo {
          childImageSharp {
            fluid {
              ...GatsbyImageSharpFluid
            }
          }
        }
        bee_name
        content
        slug
        }
      }
    }
`

I'm sure it has something to do with announcing the Icon constructor inside the marker, is it legit, what is the solution? Thanks in advance.

1 Answers

Wrap it inside the check window condition:

    return (
    {typeof window !== 'undefined' &&
      <LayoutNoPadding>
        <SEO title="WWB" />
            <Map center={[0, 0]} zoom={2} maxZoom={5} minZoom={2} attributionControl={false} >
                    <TileLayer
                    url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png"
                     />
                    {genomes.map((genome)=> {
                      return (
                        <div className="bee-icons">
                        <Marker
                          position={[`${genome.coordinate_x}`,`${genome.coordinate_y}`]}
                          icon = {new Icon({
                            iconUrl: genome.bee_icon.childImageSharp.fluid.src,
                            iconSize: [60],
                            iconAnchor: [20, 40]
                          })}
                        >
                          <Tooltip>{genome.bee_name}</Tooltip>
                          <Popup>
                            <Img fluid={genome.bee_icon.childImageSharp.fluid} alt={genome.bee_name} />
                            <h1 className="text-xl py-2">{genome.bee_name}</h1>
                            <h1 className="text-lg text-gray-500">{genome.bee_genome}</h1>
                            <p className="text-sm text-gray-700 pb-2">{genome.summary}</p>
                            <Link fade to={`/genomes/${genome.slug}`} className="flex bg-gray-200 py-2 px-4 focus:outline-none text-lg cursor-pointer text-white rounded-full justify-center text-white hover:bg-gray-300 transition-all duration-500 ease-in-out">Read More</Link>
                          </Popup>
                        </Marker>
                    </div>
                      )
                    })}
            </Map>
      </LayoutNoPadding>
    }      
 )

gatsby develop occurs in the browser-side, since it creates a socket to refresh and re-hydrate in real-time your components as well as the browser. gatsby-build occurs in the server, as you may notice, in the server, there's no window (or any other browser global objects such document) so it breaks the code. There are multiple solutions to bypass this issue: from adding a null loader to the whole bad dependency from node_modules when it's transpiled by webpack to adding a simple condition to check if the window it's defined (the solution provided).

You can check for further details in Debugging HTML builds documentation.

Related