R1ZEN / react-yandex-maps

Yandex.Maps API bindings for React (fork https://github.com/gribnoysup/react-yandex-maps)
https://pbe-react-yandex-maps.vercel.app
MIT License
120 stars 19 forks source link

Placemark fails to render on subsequent mounts #42

Closed vskolos closed 1 year ago

vskolos commented 1 year ago

I have a component with several tabs. Map is a part of one of them.

As I load the page, the map renders correctly, but if I switch the tabs back and forth, the placemark disappears, and the error message logs in the console: Uncaught TypeError: this._overlayClass is not a constructor

Here's the video with the issue:

https://user-images.githubusercontent.com/52678864/205052217-81ff56c0-805d-469b-8d03-d4fca34e35c0.mov

Simplified code of the part that switches:

{activeTab === 'building' && (
  <>
    ...
      <Map defaultState={{ center: [48.47854, 135.12157], zoom: 16 }}>
        <Placemark defaultGeometry={[48.47854, 135.12157]} />
      </Map>
    ...
  </>
}

showTab is a state variable needed to conditionally render the content of the tab, which changes when one of the buttons is clicked. Also I wrapped the whole app with <YMaps></YMaps> provider, so it is presented in the components tree as well.

After googling the error message I figured, that the issue might occur because of the Placemark component's attempt to render itself while the Map is not loaded yet.

Not sure, why it happens, because the first mount seems ok, but I think it could be enough to add some kind of a check for this condition somewhere, either in the Map component of in the child one.

Unfortunately, I have no experience with class components whatsoever, so my attempts of fixing it failed completely.

P.S. Haven't tried it, but by the looks of the code structure it seems that the same issue should be happening at least for every BaseGeoObject.

Armanio commented 1 year ago

Maybe wrap to <YMaps>?

{activeTab === 'building' && (
  <>
    ...
      <YMaps>
        <Map defaultState={{ center: [48.47854, 135.12157], zoom: 16 }}>
          <Placemark defaultGeometry={[48.47854, 135.12157]} />
        </Map>
      </YMaps>
    ...
  </>
}
vskolos commented 1 year ago

Hmm... It worked, but can you please explain why it's like that?

As far as I know, there's no difference in where you put context provider as long as it wraps your app. So what changes that behavior between these two cases?

Armanio commented 1 year ago

Truly i not fully understand whats happen. Maybe its related with incorrect destroy map, internal cache or anything? mr @R1ZEN could you explain? It bug/feature/pitfalls?

vskolos commented 1 year ago

Also, if I wrap <YMaps> around that part of the code, the map loads every time I switch the tabs. So each time a new <script> tag is added into the <head> and a new request to fetch the script fires. I don't think this is the right way of doing it.

https://user-images.githubusercontent.com/52678864/205330177-475dfc08-bb6a-42ae-afc5-ad77465a545a.mov

Screenshot 2022-12-02 at 18 40 52

Armanio commented 1 year ago

Yes. It's expected because you recreate provider (and create new api loader). I agree with you - it's look a like a bug. Could you create sandbox with demo? I try to fix it (at least investigate). 🤔

vskolos commented 1 year ago

Well, I did it, and the map works perfectly fine there: https://codesandbox.io/s/pbe-react-yandex-maps-remount-gtzgdp

I figured that I've been testing my project only locally. And magically, after uploading and running it on the server, everything worked just fine.

So it's some kind of Yandex Maps quirk when running the code locally, I guess. Thanks for the help!

R1ZEN commented 1 year ago

Yes, if you mount and unmount <YMaps/> constantly, you will produce the new <script/> tag for yandex maps api uploading. This old library behaviour that can be fixed but for now you should place your <YMaps /> to the root of your render tree or for the root of components that renders <Map>.

Behaviour with tabs and placemark looks like a bug! Need to figure out how to reproduce it.

vskolos commented 1 year ago

@R1ZEN I think you've missed my last comment :)

Well, I did it, and the map works perfectly fine there: https://codesandbox.io/s/pbe-react-yandex-maps-remount-gtzgdp

I figured that I've been testing my project only locally. And magically, after uploading and running it on the server, everything worked just fine.

So it's some kind of Yandex Maps quirk when running the code locally, I guess. Thanks for the help!

On second thought, it might work like this not because of the local environment and some weird behavior of Yandex Maps, but React's Strict Mode may make this happen as well.

In strict mode React mounts every component twice (in development mode) in order to ease debugging and make yourself sure to correctly clean up everything on unmount.

Maybe there's some inconsistency with map destruction? When the component mounts twice, something might fail to initialize for the second time because it's not destroyed after the unmount.

On the other hand, in my CodeSandbox example strict mode is active as well, but the map works fine, so maybe it's really not the reason.

Anyway, since it works fine in production, I'm fine with that :)