Reactjs 使用i18n更改语言时,使用react传单实时更新工具提示

Reactjs 使用i18n更改语言时,使用react传单实时更新工具提示,reactjs,leaflet,tooltip,geojson,i18next,Reactjs,Leaflet,Tooltip,Geojson,I18next,感谢react传单,我目前正在显示一张带有GeoJSON组件的地图。我还显示了一些悬停在一些国家和城市上的工具提示,例如,当我悬停在法国时,工具提示显示法国。我还将i18n用于国际化。 国际化对于国家/地区工具提示非常有效,它们会实时更新 我有一个函数updateDisplay,可以在缩放更改时在国家的GeoJson组件或城市的标记列表之间切换 问题是,当我切换语言时,它可以很好地用于整个页面,但不适用于城市工具提示。它们仅在缩放时更新,因此在调用updateDisplay时才会更新 我会有预期

感谢react传单,我目前正在显示一张带有GeoJSON组件的地图。我还显示了一些悬停在一些国家和城市上的工具提示,例如,当我悬停在法国时,工具提示显示法国。我还将i18n用于国际化。 国际化对于国家/地区工具提示非常有效,它们会实时更新

我有一个函数updateDisplay,可以在缩放更改时在国家的GeoJson组件或城市的标记列表之间切换

问题是,当我切换语言时,它可以很好地用于整个页面,但不适用于城市工具提示。它们仅在缩放时更新,因此在调用updateDisplay时才会更新

我会有预期的行为:无论缩放如何,我希望在切换语言时,城市工具提示能够实时更新

我希望我已经说清楚了

这是我的密码:

/**
 * Display a Leaflet Map, containing a GeoJson object, or a list of Markers, depending on the zoom
 */
export default function CustomMap(): ReactElement {
  const { t }: { t: TFunction } = useTranslation();

  const countryToString = (countries: string[]): string => countries.map(c => t(c)).join(", ");


  // Contains the json containing the polygons of the countries
  const data: geojson.FeatureCollection = geoJsonData as geojson.FeatureCollection;
  let geoJson: JSX.Element = <GeoJSON
    key='my-geojson'
    data={data}
    style={() => ({
      color: '#4a83ec',
      weight: 1,
      fillColor: "#1a1d62",
      fillOpacity: 0.25,
    })}
    onEachFeature={(feature: geojson.Feature<geojson.GeometryObject>, layer: Layer) => {
      layer.on({
        'mouseover': (e: LeafletMouseEvent) => {
          const country = countries[e.target.feature.properties.adm0_a3];
          layer.bindTooltip(countryToString(country.tooltip as string[]));
          layer.openTooltip(country.latlng);
        },
        'mouseout': () => {
          layer.unbindTooltip();
          layer.closeTooltip();
        },
      });
    }}
  />

  // Contains a list of marker for the cities
  const cityMarkers: JSX.Element[] = cities.map(
    (
      c: position,
      i: number
    ) => {
      return (
        // Here are the tooltips that doesn't update in real time, when we switch language
        // FIX ME
        <Marker key={c.latlng.lat + c.latlng.lng} position={c.latlng}>
          <Tooltip>{t(c.tooltip as string)}</Tooltip>
        </Marker>
      );
    }
  );

  const [state, setState] = useState<state>({
    zoom: 3,
    display: geoJson,
  });


  // Update on zoom change
  function onZoom(e: LeafletMouseEvent): void {
    const zoom = e.target._zoom;
    const newDisplay = updateDisplay(zoom);
    setState({
      ...state,
      zoom,
      display: newDisplay,
    });
  }

  // Called on every zoom change, in order to display either the GeoJson, or the cities Marker
  function updateDisplay(zoom: number): Marker[] | any {
    if (zoom >= 4) {
      return cityMarkers;
    } else {
      return geoJson;
    }
  }


  return (
    <Map
      style={{ height: "500px" }}
      center={[54.370138916189596, -29.918133437500003]}
      zoom={state.zoom}
      onZoomend={onZoom}
    >
      <TileLayer url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw" />
      {state.display}
    </Map>
  );
}
您也可以在此处查看:
它位于分支WIP上,您可以执行以下操作来解决此问题:

如果已添加标记,则创建一个布尔标志以保留在内存中 使用本地传单代码而不是react传单包装在地图上添加标记

如果添加了标记且缩放>=4,则将标志设置为true 如果缩放小于4,请删除标记以显示国家,将标志设置为false 更改语言时,如果缩放大于4且已添加标记,请删除之前的标记,并使用新工具提示添加新标记

您可以通过保持对映射实例的引用来实现所有这些

以下是您需要的全部代码、部分城市、删除的标记:

import React, { useState, ReactElement } from "react";
import { useTranslation } from "react-i18next";
import { Map, Marker, TileLayer, GeoJSON } from "react-leaflet";
import geoJsonData from "../assets/geoJsonData.json";
import { LatLngLiteral, Layer, LeafletMouseEvent } from "leaflet";
import geojson from "geojson";
import { TFunction } from "i18next";
import L from "leaflet";

interface position {
  latlng: LatLngLiteral;
  tooltip: string;
}

interface state {
  markers: position[];
  zoom: number;
  display: position[] | any;
  geoJson: JSX.Element;
  countries: { [key: string]: position };
}

/**
 * Display a Leaflet Map, containing a GeoJson object, or a list of Markers, depending on the zoom
 */
export default function CustomMap(): ReactElement {
  const mapRef: any = React.useRef();
  const { t, i18n }: { t: TFunction; i18n: any } = useTranslation();
  const [markersAdded, setMarkersAdded] = useState(false);

  i18n.on("languageChanged", (lng: any) => {
    if (lng) {
      const map = mapRef.current;
      if (map && map.leafletElement.getZoom() >= 4 && markersAdded) {
        map.leafletElement.eachLayer(function (layer: L.Layer) {
          if (layer instanceof L.Marker) map.leafletElement.removeLayer(layer);
        });
        state.markers.map((c: position, i: number) => {
          L.marker(c.latlng)
            .addTo(map.leafletElement)
            .bindTooltip(t(c.tooltip));
        });
      }
    }
  });

  // const countryToString = (countries: string[]): string => countries.join(", ");

  // List of position and label of tooltip for the GeoJson object, for each country
  const countries: { [key: string]: position } = {
    DEU: {
      latlng: {
        lat: 51.0834196,
        lng: 10.4234469,
      },
      tooltip: "travel.germany",
    },
    CZE: {
      latlng: {
        lat: 49.667628,
        lng: 15.326962,
      },
      tooltip: "travel.tchequie",
    },
    BEL: {
      latlng: {
        lat: 50.6402809,
        lng: 4.6667145,
      },
      tooltip: "travel.belgium",
    },
  };

  // List of position and tooltip for the cities Markers
  const cities: position[] = [
    {
      latlng: {
        lat: 48.13825988769531,
        lng: 11.584508895874023,
      },
      tooltip: "travel.munich",
    },
    {
      latlng: {
        lat: 52.51763153076172,
        lng: 13.40965747833252,
      },
      tooltip: "travel.berlin",
    },
    {
      // greece
      latlng: {
        lat: 37.99076843261719,
        lng: 23.74122428894043,
      },
      tooltip: "travel.athens",
    },
    {
      // greece
      latlng: {
        lat: 37.938621520996094,
        lng: 22.92695426940918,
      },
      tooltip: "travel.corinth",
    },
  ];

  // Contains the json containing the polygons of the countries
  const data: geojson.FeatureCollection = geoJsonData as geojson.FeatureCollection;
  let geoJson: JSX.Element = (
    <GeoJSON
      key='my-geojson'
      data={data}
      style={() => ({
        color: "#4a83ec",
        weight: 1,
        fillColor: "#1a1d62",
        fillOpacity: 0.25,
      })}
      // PROBLEM : does not update the tooltips when we switch languages
      // FIX ME
      onEachFeature={(
        feature: geojson.Feature<geojson.GeometryObject>,
        layer: Layer
      ) => {
        layer.on({
          mouseover: (e: LeafletMouseEvent) => {
            const country =
              state.countries[e.target.feature.properties.adm0_a3];
            layer.bindTooltip(t(country?.tooltip));
            layer.openTooltip(country?.latlng);
          },
          mouseout: () => {
            layer.unbindTooltip();
            layer.closeTooltip();
          },
        });
      }}
    />
  );

  const [state, setState] = useState<state>({
    markers: cities,
    zoom: 3,
    geoJson: geoJson,
    display: geoJson,
    countries: countries,
  });

  // Update on zoom change
  function onZoom(e: LeafletMouseEvent): void {
    const zoom = e.target._zoom;
    const newDisplay = updateDisplay(zoom);
    setState({
      ...state,
      zoom,
      display: newDisplay,
    });
  }

  // Called on every zoom change, in order to display either the GeoJson, or the cities Marker
  function updateDisplay(zoom: number): Marker[] | any {
    const map = mapRef.current;
    if (zoom >= 4) {
      return state.markers.map((c: position, i: number) => {
        console.log(t(c.tooltip));
        if (map && !markersAdded) {
          console.log(map.leafletElement);
          L.marker(c.latlng)
            .addTo(map.leafletElement)
            .bindTooltip(t(c.tooltip));
          setMarkersAdded(true);
        }
      });
    } else {
      map.leafletElement.eachLayer(function (layer: L.Layer) {
        if (layer instanceof L.Marker) map.leafletElement.removeLayer(layer);
      });
      setMarkersAdded(false);
      return state.geoJson;
    }
  }

  return (
    <Map
      ref={mapRef}
      style={{ height: "500px" }}
      center={[54.370138916189596, -29.918133437500003]}
      zoom={state.zoom}
      onZoomend={onZoom}
    >
      <TileLayer url='https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw' />
      {state.display}
    </Map>
  );
}
Fr:


通过分别重用标记删除代码块和标记添加代码块,您可以使其更干净。

哇,再次感谢您,它解决了我的问题。所以解决办法是在语言改变时移除并再次添加标记,就这样?你为什么用传单而不是反应传单?你知道为什么之前,我的标记工具提示没有随着语言的变化而更新,而geojson工具提示却在更新吗。您需要在工具提示中强制执行状态更新。但显然,使用react传单的包装标记并不是那么简单。有人会认为在切换语言时应该这样做。不知道问题出在哪里。对我来说,使用本机库更直接。最后,不要忘记react传单只提供传单本机代码的包装。也许可以用一种简单的方式使用react传单。但在这里,我只是展示了本地传单的方法。
"travel": {
    "germany": "Munich, Berlin, Hambourg, Münster, Allemagne",
    "munich": "Munchen",
    "berlin": "Berlin",
    "tchequie": "Tchéquie, Prague",
    "belgium": "Belgique",
    "athens": "Athènes",
    "corinth": "Corinthe",
    ...
 }
"travel": {
    "germany": "Munich, Berlin, Hamburg, Münster, Germany",
    "munich": "Munich",
    "berlin": "Berlin",
    "tchequie": "Czech Republic, Prague",
    "belgium": "Belgium",
    "athens": "Athens",
    "corinth": "Corinth",
     ...
 }