React

Важно

Поддерживаемая версия React JS: не ниже 16.

JS API поддерживает интеграцию только с React JS.

На данный момент поддержки React Native нет.

Для каждого императивного класса API, который наследуется от YMapEntity, есть React-аналог. Чтобы воспользоваться React-версией API, подключите модуль @yandex/ymaps3-reactify:

const ymaps3Reactify = await ymaps3.import('@yandex/ymaps3-reactify');
const reactify = ymaps3Reactify.reactify.bindTo(React, ReactDOM);
const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = reactify.module(ymaps3);

Модуль @yandex/ymaps3-reactify предоставляет набор методов для обращения в React как отдельных объектов, так и модулей/пакетов целиком. Иерархия объектов и параметры инициализации для большинства классов те же самые.

Подключив модуль, используйте объекты-наследники YMapEntity как React-компоненты:

<YMap location={{center: [37.588144, 55.733842], zoom: 9}} mode="vector">
  <YMapDefaultSchemeLayer />
  <YMapDefaultFeaturesLayer />

  <YMapMarker coordinates={[37.588144, 55.733842]} draggable={true}>
    <section>
      <h1>You can drag this header</h1>
    </section>
  </YMapMarker>
</YMap>

Быстрый старт

Подключение через top-level-await

<!DOCTYPE html>
<html>
  <head>
    <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(<App />);
import {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker, reactify} from './lib/ymaps3';
import type {YMapLocationRequest} from 'ymaps3';

const LOCATION: YMapLocationRequest = {
  center: [37.588144, 55.733842],
  zoom: 9
};

export default function App() {
  return (
    <div style={{width: '600px', height: '400px'}}>
      <YMap location={reactify.useDefault(LOCATION)}>
        <YMapDefaultSchemeLayer />
        <YMapDefaultFeaturesLayer />

        <YMapMarker coordinates={reactify.useDefault([37.588144, 55.733842])} draggable={true}>
          <section>
            <h1>You can drag this header</h1>
          </section>
        </YMapMarker>
      </YMap>
    </div>
  );
}
import React from 'react';
import ReactDom from 'react-dom';

const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);

export const reactify = ymaps3React.reactify.bindTo(React, ReactDom);
export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} = reactify.module(ymaps3);
{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "typeRoots": ["./node_modules/@types", "./node_modules/@yandex/ymaps3-types"],
    "paths": {
      "ymaps3": ["./node_modules/@yandex/ymaps3-types"]
    }
  }
}
{
  "devDependencies": {
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@yandex/ymaps3-types": "^0.0.27",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.5"
  },
  "scripts": {
    "start": "react-scripts start"
  }
}

Поставьте зависимости и запустите локальный сервер:

npm install
npm start

Откройте приложение

Особенности

  1. В package.json добавляем dev-зависимость от пакета @yandex/ymaps3-types.

    Рекомендуется устанавливать последнюю версию:

    npm i --save-dev @yandex/ymaps3-types@latest

  2. В tsconfig.json задаём compilerOptions.typeRoots со списком путей к файлам типов. Добавляем туда путь к пакету @yandex/ymaps3-types, благодаря чему в глобальной области видимости появляется пространство имен ymaps3 с типами.

    Примечание

    Пространство имен ymaps3 содержит все типы классов, которые предоставляет JS API, но до резолва ymaps3.ready в среде выполнения они недоступны.

  3. В tsconfig.json задаём compilerOptions.paths, в которой сообщаем ts-компилятору о том, что при импорте пакета ymaps3 его контент следует искать по указанному пути. Благодаря этому в проектных файлах можно импортировать типы словно они лежат не в @yandex/ymaps3-types, а в пакете ymaps3:

    import type {YMapLocationRequest} from 'ymaps3';
    

    Все типы должны быть импортированы из корня.

    Внутренняя структура не гарантирована и может меняться со временем.

  4. В tsconfig.json, для корректной работы top-level-await, параметр compilerOptions.module должен быть установлен в одно из следующих значений: es2022, esnext, system или preserve. Также параметр compilerOptions.target должен быть es2017 или выше.

  5. Подключите модуль @yandex/ymaps3-reactify. В файле lib/ymaps3.ts дожидаемся полной загрузки JS API и reactify модуля, после чего экспортируем необходимые компоненты карты для их использования в других частях проекта:

    import React from 'react';
    import ReactDom from 'react-dom';
    
    const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
    
    export const reactify = ymaps3React.reactify.bindTo(React, ReactDom);
    export const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = reactify.module(ymaps3);
    

    Примечание

    Модуль @yandex/ymaps3-reactify предоставляет набор методов для доступа к React как к отдельным объектам, так и к модулям/пакетам в целом. Иерархия объектов и параметры инициализации одинаковы.

  6. Использование top-level-await в lib/ymaps3.ts гарантирует выполнение ymaps3.ready и ymaps3.import('@yandex/ymaps3-reactify') до импорта компонентов карты, что позволяет синхронно использовать любые объекты JS API в качестве компонентов React:

    <YMap location={reactify.useDefault(LOCATION)}>
      <YMapDefaultSchemeLayer />
      <YMapDefaultFeaturesLayer />
      ...
    </YMap>
    

reactify.useDefault

YMap и все остальные компоненты – неконтролируемые. Компоненты используют императивный интерфейс библиотеки (например, YMapZoomControl вызывает YMap.update({location})). Это может вызывать рассинхронизацию с состоянием и параметрами в React (например location у YMap или coordinates у YMapMarker с включенным перетаскиванием).

Используйте reactify.useDefault(value), чтобы задать параметр компонента только один раз и не обновлять его при ререндерах. Например, <YMap location={reactify.useDefault({center, zoom})}/> будет вести себя как <input defaultValue={''}/>.

Чтобы контролировать обновление параметров, используйте второй параметр reactify.useDefault(value, deps) – массив зависимостей как в React hook'ах (например, useCallback, useMemo, useEffect). Параметр обновится, если массив зависимостей будет отличаться.

Для параметров с объектами, само значение может быть использовано как зависимость. Ререндер произойдет только, если значения будут отличаются. Например const [location, setLocation] = useState(...), reactify.useDefault(location, [location]), setLocation({...}).

reactify.useDefault работает с любыми параметрами любых компонентов из reactify.

Важно

reactify.useDefault возвращает объект без публичного контракта и должен быть использован только напрямую в параметрах компонентов.

Пользовательские реализации объектов ymaps3.YMapEntity для React

Используйте ymaps3.overrideKeyReactify ключ, чтобы определить пользовательскую реализацию объектов ymaps3.YMapEntity для reactify:

type YMapSomeClassProps = {
  id: string;
};
export class YMapSomeClass extends ymaps3.YMapComplexEntity<YMapSomeClassProps> {
  static [ymaps3.overrideKeyReactify] = YMapSomeClassReactifyOverride;
  //...
}

и метод для определения пользовательской реализации:

export const YMapSomeClassReactifyOverride = (
  YMapSomeClassI, // базовый класс YMapSomeClass
  {reactify, React}
) => {
  const YMapSomeClassReactified = reactify.entity(YMapSomeClassI); // Стандартный reactify метод
    const YMapSomeClassR = React.forwardRef((props, ref) => {
      return (<>
        <YMapSomeClassReactified {...props} ref={ref} ... />
      </>);
    })
  return YMapSomeClassR;
}

и добавим полученный компонент в приложение:

import {YMapSomeClass} from './some-class';
import React from 'react';
import ReactDOM from 'react-dom';
// ...
const ymaps3Reactify = await ymaps3.import('@yandex/ymaps3-reactify');
const reactify = ymaps3Reactify.reactify.bindTo(React, ReactDOM);
const YMapSomeClassR = reactify.entity(YMapSomeClass);

function App() {
  return <YMapSomeClassR id="some_id" />;
}
Предыдущая
Следующая