Благодаря ответу kboul в этом и моем другом вопросе я собираюсь выписать ответ здесь. Это на самом деле ответ kboul, но я хочу закрепить его в своем мозгу, написав его, и сделать его доступным для всех, кто запинается.
Во-первых, нам нужно создать объект контекста и поставщика для этот контекст. Мы создадим два новых файла в каталоге для легкого доступа из других файлов:
/src
-App.js
-Map.js
-MapContext.js
-MapProvider.js
-Sibling.js
-Niece.js
/Components
-ZoomControl.js
-OtherControls.js
Создайте пустой объект контекста:
// MapContext.jsx
import { createContext } from "react";
const MapContext = createContext();
export default MapContext
Далее мы используем объект MapContext для создания MapProvider. MapProvider имеет свое собственное состояние, которое содержит пустую ссылку, которая станет ссылкой на карту. Он также имеет метод setMap
для установки ссылки на карту в своем состоянии. Он предоставляет в качестве значения пустую ссылку, а также метод для установки ссылки на карту. Наконец, он отображает свои дочерние элементы:
// MapProvider.jsx
import React from "react";
import MapContext from "./MapContext";
class MapProvider extends React.Component {
state = { map: null };
setMap = map => {
this.setState({ map });
};
render() {
return (
<MapContext.Provider value={{ map: this.state.map, setMap: this.setMap }}>
{this.props.children}
</MapContext.Provider>
);
}
}
export default MapProvider;
Теперь в компоненте Map мы экспортируем карту, обернутую в MapProvider.
// Map.jsx
import React from "react";
import { Map as MapComponent, TileLayer, Marker, etc } from 'react-leaflet'
import MapContext from './MapContext'
class Map extends React.Component{
mapRef = React.createRef(null);
componentDidMount() {
const map = this.mapRef.current.leafletElement;
this.props.setMap(map);
}
render(){
return (
<MapComponent
center={[centerLat, centerLng]}
zoom={11.5}
...all the props
ref={this.mapRef} >
</MapComponent>
);
}
}
const LeafletMap = props => (
<MapContext.Consumer>
{({ setMap }) => <Map {...props} setMap={setMap} />}
</MapContext.Consumer>
)
export default LeafletMap
На этом последнем шаге мы не будем Экспорт карты, а мы экспортируем карту, завернутую в поставщика, с {value}
MapProvider в качестве реквизита карты. Таким образом, когда LeafletMap
вызывается в компоненте App
, в componentDidMount функция setMap
будет вызываться как опора, вызывая функцию MapProvider
setMap. Это устанавливает состояние MapProvider
, чтобы иметь ссылку на карту. Но этого не произойдет, пока карта не будет отображена в App
:
// App.js
class App extends React.Component{
state = { mapLoaded: false }
componentDidMount(){
this.setState({ mapLoaded:true })
}
render(){
return (
<MapProvider>
<LeafletMap />
{this.state.mapLoaded && <Sibling/>}
</MapProvider>
)
}
}
Обратите внимание, что метод MapProvider setMap
не вызывается до тех пор, пока не сработает LeafletMap
componentDidMount. Таким образом, при рендеринге в App
значение контекста еще не существует, и у любого компонента в Sibling
, который пытается получить доступ к контексту, его еще не будет. Но после запуска рендеринга App
и запуска LeafletMaps
componentDidMount запускается setMap
, и значение map
означает, что поставщик доступен. Поэтому в App
мы ждем, пока будет запущен componentDidMount, и в этот момент setMap
уже запущен. Мы устанавливаем состояние в App
, в которое загружена карта, наш оператор условного рендеринга для Sibling
будет отображать Sibling
со всеми его дочерними объектами с объектом MapContext, правильно ссылающимся на карту. Теперь мы можем использовать его в компоненте. Например, я переписал компонент GeoSearch, чтобы он работал так (благодаря предложению kboul):
// GeoSearch
import React from 'react'
import MapContext from '../Context'
import * as ELG from "esri-leaflet-geocoder";
class EsriGeoSearch extends React.Component {
componentDidMount() {
const map = this.mapReference
const searchOptions = {
...this.props,
providers: this.props.providers ? this.props.providers.map( provider => ELG[provider]()) : null
};
const GeoSearch = new ELG.Geosearch(searchOptions);
const searchContainer = GeoSearch.onAdd(map);
document.querySelector('geocoder-control-wrapper').appendChild(searchContainer);
}
render() {
return (
<MapContext.Consumer>
{ ({map}) => {
this.mapReference = map
return <div className='geocoder-control-wrapper' EsriGeoSearch`} />
}}
</MapContext.Consumer>
)
}
}
export default EsriGeoSearch;
Итак, все, что мы здесь сделали, - это создали объект Esri GeoSearch и сохранили его HTML и связанные с ним обработчики в переменная searchContainer
, но не добавила ее на карту. Вместо этого мы создаем контейнер div, где мы хотим его в нашем дереве DOM, а затем на componentDidMount мы визуализируем HTML внутри этого контейнера div. Таким образом, у нас есть компонент, который написан и отрисован в предназначенном для него месте в приложении, который правильно связывается с картой.
Извините за долгое чтение, но я хотел выписать ответ, чтобы укрепить свои собственные знания и иметь достаточно канонический ответ для любого, кто может оказаться в такой же ситуации в будущем. Кредит идет на 100%, я просто обобщаю его ответы в одном месте. У него есть рабочий пример здесь . Если вам нравится этот ответ, пожалуйста, проголосуйте за его недовольство.