ymaps3 - Карта ре-инициализируется при любой манипуляции с DOM
Пытаюсь сделать обертку для Yandex Map Api 3 для реакта.
Создаю контекст, в контексте через useSyncExteranlStore загружаю скрипт.
Получаю экземпляр yamps
и reactify
:
const YMapsProvider = ({
apikey,
lang,
children,
}: YMapsProviderProps): JSX.Element => {
const state = useSyncExternalStore(
store.subscribe,
() => store.getState(),
() => ({
reactify: null,
ymaps: null,
})
);
useIsomorphicEffect(() => {
if (apikey) initializeAction({ apikey, lang });
}, [apikey, lang]);
return (
<ymapsContext.Provider
value={{
ymaps: state.ymaps,
reactify: state.reactify,
}}
>
{children}
</ymapsContext.Provider>
);
};
Дальше внутри компонентов карт читаю контекст и выпускаю экземпляр компонента:
export const YMap: typeof YMapReact = ({ ...props }) => {
const { reactify, ymaps } = useYMapsContext();
if (!reactify || !ymaps) return null;
const Component = reactify.module(ymaps)["YMap"];
return <Component {...props} />;
};
С этим все хорошо, но любая манипуляция, например setState вызывает реинициализацию карты.
Через несколько манипуляций в окне браузера вылетает warning
:
WARNING: Too many active WebGL contexts. Oldest context will be lost.
Пробовал создавать компоненты через useMemo, useCallback, createElement, forwardRef и т.п., передавать yamps
и reactify
как рефы т.п.
Судя по всему либо в контексте чего-то не хватает, либо...
Буду признателен за любые идеи
Репозиторий: https://github.com/antokhio/next-ymaps3-issue
Постарался максимально убрать лишний код.
Ответы (1 шт):
При помощи поддержки Яндекс карт удалось решить проблему:
Суть в том, что reactify
выпускает новый инстанс компонента при каждом обращении, поэтому меморизировать необходимо именно его:
import { YMap as YMapReact } from "@yandex/ymaps3-types/react";
import { useYMapsContext } from "./YMapsProvider";
import React, { useMemo } from "react";
export const YMap = React.forwardRef(({ ...props }: any, ref: any) => {
const value = useYMapsContext();
const Component: typeof YMapReact | null = useMemo(() => {
if (value.reactify && value.ymaps) {
return value.reactify.module(value.ymaps)["YMap"];
} else return null;
}, [value.reactify]);
return Component ? (
<Component ref={ref} {...props} />
) : (
<>loading</>
);
});
YMap.displayName = "YMap";
Рабочая версия.