Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library Components built with @react-leaflet/core cannot useLeafletContext #897

Closed
7 tasks done
miccoh1994 opened this issue Jul 6, 2021 · 26 comments
Closed
7 tasks done

Comments

@miccoh1994
Copy link

miccoh1994 commented Jul 6, 2021

Bug report

Before opening an issue, make sure to read the contributing guide and understand this is a bug tracker, not a support platform.

Please make sure to check the following boxes before submitting an issue.
Issues opened without using this template will be closed unless they have a good reason not to follow this template.

  • All peer dependencies are installed: React, ReactDOM and Leaflet.
  • Using a supported version of React and ReactDOM (v17.0.0 minimum).
  • Using the supported version of Leaflet (v1.7.1 minimum) and its corresponding CSS file is loaded.
  • Using the latest v3 version of React-Leaflet.
  • The issue has not already been reported.
  • Make sure you have followed the quick start guide for Leaflet.
  • Make sure you have fully read the documentation and that you understand the limitations.

Expected behavior

Expect component built with @react-leaflet/core to render.

Actual behavior

React app will not compile with message:
No context provided: useLeafletContext() can only be used in a descendant of <MapContainer>

Steps to reproduce

  1. create custom component:
import { createPathComponent } from '@react-leaflet/core';
import L from 'leaflet';

function getBounds(props) {
  return L.latLng(props.center).toBounds(props.size)
}

function createSquare(props, context) {
  const instance = new L.Rectangle(getBounds(props))
  return { instance, context: { ...context, overlayContainer: instance } }
}

function updateSquare(instance, props, prevProps) {
  if (props.center !== prevProps.center || props.size !== prevProps.size) {
    instance.setBounds(getBounds(props))
  }
}

const Square = createPathComponent(createSquare, updateSquare)

export default Square;
  1. Publish component or link to component from another respository
  2. Use component in
  3. Get Error: No context provided: useLeafletContext() can only be used in a descendant of <MapContainer>

https://codepen.io/MichaelACohen/pen/yLbewVX - example using someones MarkerClusterGroup

@PaulLeCam
Copy link
Owner

Please provide an example codepen that doesn't use a third-party library to reproduce the issue.

@jhotadhari
Copy link

Got the same error after updating react-leaflet from 3.2.1 to 3.2.2.
I rolled back the update to 3.2.1. Now the map renders again.


Recognized that the issue is somehow with the react-leaflet-fullscreen-plugin. When I remove the plugin, react-leaflet 3.2.2 works well.

@piotr-cz
Copy link

Got the error after updating react-leaflet from 3.2.1 to 3.2.2.
I'm also using react-leaflet-markercluster

@smcalilly
Copy link

I'm having the same issue when trying to use react-leaflet-draw in a working react-leaflet map. Downgrading to 3.2.1 didn't fix the issue.

@MarceloDiazz
Copy link

@smcalilly tengo el mismo problema, utilizo react leaflet 2.8 y react leaflet draw 0.19.0

@MarceloDiazz
Copy link

@smcalilly pudiste solucionar el error??

@neelduttahere
Copy link

I'm having the same issue when trying to use react-leaflet-draw in a working react-leaflet map. Downgrading to 3.2.1 didn't fix the issue.

Same here. I downgraded to 3.21,

according to this thread: yuzhva/react-leaflet-markercluster#156
I should try to downgrade it further to 3.0.2

@neelduttahere
Copy link

Still does not help

neelduttahere added a commit to neelduttahere/pixxel-assignment that referenced this issue Apr 17, 2022
1. Completed actionables
2. Tailwind configured
3. Leaflet is also configured with react-leaflet

Issues:
1. React-leaflet-draw is having runtime issues, link to a similar issue: PaulLeCam/react-leaflet#897
@manuelpoelzl
Copy link

I´m also facing this issue when i use the useMap() hook

@amauryfischer
Copy link

Working when i downgrade to :

  • react < 18 (because react 18 works only with react-leaflet < v4 released a few days ago
  • "react-leaflet": "3.1.0",
  • "react-leaflet-control": "^2.1.2",
  • "react-leaflet-draw": "^0.19.8",
  • "react-leaflet-markercluster": "^3.0.0-rc1",

@l4b4r4b4b4
Copy link

l4b4r4b4b4 commented Apr 24, 2022

@amauryfischer
Did you downgrade React to a version lower than 18 as well or just the leaflet libs?

Getting the same issue with

  • NextJS 12,
  • ReactJS 18,
  • react-leaflet 4.0.0
  • react-leaflet-markercluster 3.0.0-rc1
  • react-universal 2.2.1

@amauryfischer
Copy link

@l4b4r4b4b4 Yes i downgraded to React 17 from 18 because only react leaflet v4 is compatible with react 18

@l4b4r4b4b4
Copy link

yeah, I was afraid that was the case and figured it out by trying too ^^
Well I guess I will wait until react-marker-cluster upgrades to react-leaflet v4...
Have you seen any info whether someone is working on that?

@amauryfischer
Copy link

@l4b4r4b4b4 not yet unfortunately :/ guess we will have to do it ourself if nothing is done in coming month

@arnaldocrescente
Copy link

Same issue on upgrading react-leaflet to v4.

  • @geoman-io/leaflet-geoman-free": "^2.11.4"
  • "@react-leaflet/core": "^1.1.1"
  • "@types/leaflet": "^1.7.9"
  • "leaflet": "^1.8.0"
    -"react": "^18.1.0"
  • "react-leaflet": "^4.0.0"

@arnaldocrescente
Copy link

Same issue on upgrading react-leaflet to v4.

  • @geoman-io/leaflet-geoman-free": "^2.11.4"
  • "@react-leaflet/core": "^1.1.1"
  • "@types/leaflet": "^1.7.9"
  • "leaflet": "^1.8.0"
    -"react": "^18.1.0"
  • "react-leaflet": "^4.0.0"

Solved by upgrading @react-leaflet/core to v2.0.0

@l4b4r4b4b4
Copy link

l4b4r4b4b4 commented Apr 28, 2022

ui there is hope :)
Will check and report back!

Nope so far no luck with that...

@l4b4r4b4b4
Copy link

ui there is hope :) Will check and report back!

Nope so far no luck with that...

Digging down on the installed react-marker-cluster in node_modules I found the following in package.json of the module:

{
  "name": "react-leaflet-markercluster",
  "version": "3.0.0-rc1",
  "description": "React wrapper of Leaflet.markercluster for react-laeflet",
  "main": "dist/index.js",
  "repository": "git@github.com:YUzhva/react-leaflet-markercluster.git",
  "keywords": [
    "react",
    "leaflet",
    "react-leaflet",
    "markercluster",
    "marker",
    "cluster"
  ],
  "author": "Yevhen Uzhva <yevhen.uzhva@gmail.com>",
  "license": "MIT",
  "private": false,
  "scripts": {
    "dev": "start-storybook -p 8080",
    "lint": "eslint --ext .js ./ --ignore-pattern '/dist/'",
    "build:code": "babel src -d dist && terser ./dist/react-leaflet-markercluster.js -o dist/index.js && rm ./dist/react-leaflet-markercluster.js",
    "build:styles": "node-sass src/styles.scss dist/styles.min.css --output-style compressed",
    "build:source": "yarn build:code && yarn build:styles",
    "build:gh-pages": "build-storybook",
    "deploy:gh-pages": "gh-pages -d storybook-static"
  },
  "devDependencies": {
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.9.0",
    "@babel/preset-env": "^7.9.0",
    "@storybook/addon-actions": "^5.3.18",
    "@storybook/addon-docs": "^5.3.18",
    "@storybook/preset-scss": "^1.0.2",
    "@storybook/react": "^5.3.18",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.4.2",
    "eslint": "^6.8.0",
    "eslint-config-airbnb": "^18.1.0",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-jsx-a11y": "^6.2.3",
    "eslint-plugin-react": "^7.19.0",
    "eslint-plugin-react-hooks": "^2.5.1",
    "gh-pages": "^2.2.0",
    "husky": "^4.2.3",
    "node-sass": "^4.13.1",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.1.3",
    "terser": "^4.6.10"
  },
  "dependencies": {
    **"@react-leaflet/core": "^1.0.2",**
    **"leaflet": "^1.6.0",**
    **"leaflet.markercluster": "^1.4.1",**
    **"react-leaflet": "^3.0.0"**
  },
  "peerDependencies": {
    **"leaflet": "^1.6.0",**
    **"leaflet.markercluster": "^1.4.1",**
    **"react-leaflet": "^3.0.0"**
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn lint"
    }
  }
}

My Project setup has the following though:

leaflet: "^1.7.1",
leaflet.markercluster: "^1.5.3",
@react-leaflet/core: 2.0
react-leaflet: 4.0.0
react-leaflet-markercluster: 3.0.0-rc1

Maybe package dependencies of the module simply need to be updated and it will work?

@seyyed-sina
Copy link

@PaulLeCam It seems it has conflict with some third-party modules such as: react-leaflet-markercluster. I also downgrade react-leafet to v3.0.5 and still get this error due to unknown reason! Please notice the current package.json module version below:

"react": "^17.0.2",
"react-dom": "^17.0.2",
"leaflet": "^1.8.0",
"react-leaflet": "^4.0.0",
"react-leaflet-markercluster": "^3.0.0-rc1",

@amauryfischer
Copy link

@l4b4r4b4b4 i tried to fork react leaflet markercluster and update its dependecies to react leaflet v4 and react-leaflet core v2 with a fork : https://github.com/amauryfischer/react-leaflet-markercluster
but i have the same error :/

@l4b4r4b4b4
Copy link

l4b4r4b4b4 commented May 10, 2022

Thats unlucky.

I currently have my head stuck in authentication and authorization with keycloak, openLDAP and next-auth, which needs most of my attention, but will quickly have a look at it.

My Code Fork

Storybook Github Page Deployment

Update 1

I noticed quite a few other packages needed an upgrade as well.
but cant seem to get the storybook running with yarn run dev.
First I got

ERR! Error: Cannot find module 'react/package.json'

I noticed react and react-dom missing as dependencies, which I found odd, so I added them as dev dependencies as stated in this github issue. Now I get different compilation errors especially for not having an appropriate file loader:
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

ERROR in ./.storybook/examples/styles.scss
Module build failed (from ./node_modules/style-loader/dist/cjs.js):
TypeError: this.getOptions is not a function

Update 2

  1. Removed package-lock.json node_modules
  2. I upgraded all other dependencies with yarn upgrade --latest
  3. Initialized project with yarn
  4. Initialized storybook with npx sb init
  5. Upgraded and migrated storybook with npx sb upgradand npx sb@next automigrate

Still same error 😢

Update 3

Got the storybook to run. Here are the steps as far as I remember them:

  1. Upgrade storybook to the latest version npx sb upgrade
  2. Added Webpack 5 yarn add @storybook/builder-webpack5 and dev dependencies yarn add -D @storybook/builder-webpack5@next @storybook/manager-webpack5@next webpack@5
  3. Added builder to .storybook/main.js
module.exports = {
  stories: ["./**/*.stories.(js|mdx)"],
  presets: ["@storybook/preset-scss"],
  addons: ["@storybook/addon-actions/register", "@storybook/addon-docs"],
  core: {
    builder: "webpack5",
  },
  1. Initialize builder npx sb init --builder webpack5
  2. Run storybook in dev mode yarn dev
    grafik

Update 4

  1. Build code yarn build:code
  2. Build style yarn build:style
  3. Build source yarn build:source

When simply importing the code into react-leaflet-markercluster in node_modules I now get a different error:
grafik

It complains about invalid hook calls as well as the map context. I guess the issues is around how ctxis obtained in react-marker-cluster. This article seems to cover a similar topic. Let's see if it helps.

Update 5

I think I'm close. I refactored the whole code interfacing to leaflet.markercluster in the style of the article referred to above. Now I'm stuck at Leaflet not loading leaflets DistantGrid contructor.
ClusterMarker.jsx:

TypeError: L.DistanceGrid is not a constructor

No matter whether trying the @react/leaflet/core hooks or plain implementation in useEffect hook.

import React, { useEffect } from 'react'
import L from 'leaflet'
import 'leaflet-ellipse'
import {
  createElementHook,
  createPathHook,
  createContainerComponent,
} from '@react-leaflet/core'
import 'leaflet.markercluster'
import 'leaflet.markercluster.layersupport'

function createClusterMarker(clusterProps, context) {
  const instance = L.markerClusterGroup.layerSupport(clusterProps)
  // const instance =  new L.MarkerClusterGroup(clusterProps)
  return {
    instance,
    context: {
      ...context,
      overlayContainer: instance,
    },
  }
}

const ClusterMarker = createPathComponent(createClusterMarker)

export default ClusterMarker

TestMap.jsx:

import { useState } from 'react'
import {
  MapContainer,
  TileLayer,
  LayersControl,
  Marker,
  useMapEvent,
} from 'react-leaflet'

import * as L from 'leaflet'

import 'leaflet/dist/leaflet.css'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css' // Re-uses images from ~leaflet package
import 'react-leaflet-markercluster/dist/styles.min.css' // sass
import 'leaflet-defaulticon-compatibility'

import MinimapControl from './MinimapControl'
import ClusterMarker from './ClusterMarker'

const INITIAL_MAP_CONFIG = {
  center: [47.120926584890945, 9.518731725025518],
  zoom: 3,
}
function SetViewOnClick() {
  const map = useMapEvent('click', (e) => {
    map.setView(e.latlng, map.getZoom(), {
      animate: true,
    })
  })

  return null
}

function MapPlaceholder() {
  return (
    <p>
      Interaktive Ressourcenkarte.{' '}
      <noscript>
        Bitte aktivieren Sie JavaScript, um die interaktive Karte anzuzeigen.
      </noscript>
    </p>
  )
}

const TestMap = (props) => {
  const [map, setMap] = useState(null)

  return (
    <MapContainer
      className='markercluster-map'
      center={INITIAL_MAP_CONFIG.center}
      zoom={INITIAL_MAP_CONFIG.zoom}
      minZoom={1.25}
      scrollWheelZoom={true}
      whenCreated={(map) => setMap(map)}
      style={{ height: 300, width: '90%', padding: 0 }}
      placeholder={<MapPlaceholder />}
    >
      <MinimapControl position='topright' />

      <LayersControl position='bottomright'>
        <LayersControl.BaseLayer checked name='Weltkarte (topologisch)'>
          {/* <LayersControl.BaseLayer checked name='OpenStreetMap.Physic'> */}
          <TileLayer
            // attribution='Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
            url='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
          />
        </LayersControl.BaseLayer>

        <LayersControl.Overlay name='MarkerCluster' checked>
          <ClusterMarker>
            <Marker position={[49.8397, 24.0297]} />
            <Marker position={[52.2297, 21.0122]} />
            <Marker position={[51.5074, -0.0901]} />
          </ClusterMarker>
        </LayersControl.Overlay>
      </LayersControl>

      <SetViewOnClick />
    </MapContainer>
  )
}

export default TestMap

Update 6

Ok. I got it to compile without errors with above code. Passing an empty update function to `` seems to have been causing the constructor issue... But the cluster feature still shines with absence 😜
grafik

My guess I would have to take a closer look at the needed props and potentially leaflet.markcluster's update function.
To be continued...

@l4b4r4b4b4
Copy link

l4b4r4b4b4 commented May 10, 2022

Good news everyone! I got it to work. 😸
grafik

I guess I will make a pull request to the original repo.

My Setup

  • docker-compose on Debian
  • "next": "^12.1.5",
  • "react-dom": "^18.1.0",
  • "react": "^18.1.0",
  • "leaflet": "^1.8.0",
  • "leaflet-defaulticon-compatibility": "^0.1.1",
  • "leaflet.markercluster": "^1.5.3",
  • "@react-leaflet/core": "^2.0.0",
  • "@types/leaflet": "^1.7.9",
  • "react-leaflet": "^4.0.0",

Refactored Component

import L from 'leaflet'
import { createPathComponent } from '@react-leaflet/core'
import 'leaflet.markercluster'

function createClusterMarker({ children: _c, ...props }, context) {
  const clusterProps = {}
  const clusterEvents = {}
  // Splitting props and events to different objects
  Object.entries(props).forEach(([propName, prop]) =>
    propName.startsWith('on')
      ? (clusterEvents[propName] = prop)
      : (clusterProps[propName] = prop)
  )
  const instance = new L.MarkerClusterGroup(clusterProps)

  // Initializing event listeners
  Object.entries(clusterEvents).forEach(([eventAsProp, callback]) => {
    const clusterEvent = `cluster${eventAsProp.substring(2).toLowerCase()}`
    instance.on(clusterEvent, callback)
  })
  return {
    instance,
    context: {
      ...context,
      layerContainer: instance,
    },
  }
}

const ClusterMarker = createPathComponent(createClusterMarker)

export default ClusterMarker

Is it possible to only import a subset of leaflet and not the whole package?
grafik

l4b4r4b4b4 added a commit to l4b4r4b4b4/react-leaflet-markercluster that referenced this issue May 11, 2022
As documented here (PaulLeCam/react-leaflet#897 (comment)) several people me included had difficulties making it work with newer versions of the ecosystem. I finally got it to work with above code and following system settings.

    docker-compose on Debian
    "next": "^12.1.5",
    "react-dom": "^18.1.0",
    "react": "^18.1.0",
    "leaflet": "^1.8.0",
    "leaflet-defaulticon-compatibility": "^0.1.1",
    "leaflet.markercluster": "^1.5.3",
    "@react-leaflet/core": "^2.0.0",
    "@types/leaflet": "^1.7.9",
    "react-leaflet": "^4.0.0",
@barbalex
Copy link

barbalex commented Jul 5, 2022

I tried @changey/react-leaflet-markercluster (forked from https://github.com/l4b4r4b4b4/react-leaflet-markercluster) and it seems to work fine as a drop-in replacement.

@barbalex
Copy link

barbalex commented Aug 24, 2022

When markercluster is used, I now sometimes get this error:
2022-08-24_13h11_02

@l4b4r4b4b4 Maybe @react-leaflet/core and react-leaflet need to be updated in https://github.com/changey/react-leaflet-markercluster?

I deleted yarn.lock, deleted node_modules, then ran yarn. Now the error does not happen anymore. I guess that is because the previously as dependencies of https://github.com/changey/react-leaflet-markercluster installed older versions of react-leaflet caused this error.

As they are preceded by ^ in the package.json, this should not happen with new installations. But did with installations previous to the last update of react-leaflet/core (2.0.1) and react-leaflet (1.0.2).

@amauryfischer
Copy link

I tried @changey/react-leaflet-markercluster (forked from https://github.com/l4b4r4b4b4/react-leaflet-markercluster) and it seems to work fine as a drop-in replacement.

I can confirm this is working great with all last version of react-leaflet and leaflet

@l4b4r4b4b4
Copy link

I am glad original maintainer confirms :D

@rhayart
Copy link

rhayart commented Jul 12, 2023

If some people still get this error after upgrading react-leaflet and/or leaflet, i manage to fix it by removing the plugin (in my case the marker cluster plugin) and re-add it.
It appears that it creates conflict in packages version.

@piotr-cz
Copy link

piotr-cz commented Jun 26, 2024

Example above were not working for me with preact, so this is what I came up with:

// MarkerClusterGroup.tsx
import { type MarkerClusterGroupOptions, MarkerClusterGroup as LeafletMarkerClusterGroup } from 'leaflet'
import { type EventedProps, createLayerComponent, createElementObject, extendContext } from '@react-leaflet/core'

export interface MarkerClusterGroupProps extends MarkerClusterGroupOptions, EventedProps {
  children?: React.ReactNode
}

export const MarkerClusterGroup = createLayerComponent<LeafletMarkerClusterGroup, MarkerClusterGroupProps>(
  function createMarkerCluster({ children: _c, eventHandlers, ...options }, context) {
    const markerClusterGroup = new LeafletMarkerClusterGroup(options)

    return createElementObject(markerClusterGroup, extendContext(context, { layerContainer: markerClusterGroup }))
  },
  function updateMarkerCluster(markerClusterGroup, props, prevProps) {
    if (props.children !== prevProps.children) {
      markerClusterGroup.refreshClusters()
    }
  }
)

Usage:

// MapPage.tsx
export const MapPage: FunctionComponent<{
  devices:  Array<{ id: string, position: { lat: number, lng: number } }>
}> = ({
  devices
}) => 
  <MapContainer>
    <MarkerClusterGroup
      eventHandlers={{
        // @ts-ignore - Cluster events are not typed, but that's a different story
        clusterclick: () => console.log('click')
      }}
    >
      {devices.map(device =>
        <Marker key={device.id} position={device.position}>
      )}
    </MarkerClusterGroup>
  </MapContainer>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests