Thêm bản đồ và điểm đánh dấu vào ứng dụng phản ứng

Tổng quan

Hướng dẫn này cho bạn biết cách thêm Bản đồ và Điểm đánh dấu vào ứng dụng React bằng cách sử dụng @googlemaps/react-wrapper và tích hợp bản đồ và điểm đánh dấu vào trạng thái ứng dụng.

Cài đặt @googlemaps/react-wrapper

Cài đặt và sử dụng thư viện @googlemaps/react-wrapper để tự động tải API JavaScript của Maps khi thành phần được hiển thị.

npm install @googlemaps/react-wrapper

Bạn có thể nhập và sử dụng thư viện này với thư viện sau.

import { Wrapper, Status } from "@googlemaps/react-wrapper";

Cách sử dụng cơ bản của thành phần này là để gói các thành phần con phụ thuộc vào API JavaScript của Maps. Thành phần Wrapper cũng chấp nhận đạo cụ render để kết xuất các thành phần tải hoặc xử lý lỗi khi tải API JavaScript của Maps.

const render = (status: Status) => {
  return <h1>{status}</h1>;
};

<Wrapper apiKey={"YOUR_API_KEY"} render={render}>
  <YourComponent/>
</Wrapper>

Thêm thành phần bản đồ

Một thành phần chức năng cơ bản để kết xuất bản đồ có thể sẽ sử dụng các móc phản ứng useRef, useStateuseEffect.

Thành phần bản đồ ban đầu sẽ có chữ ký sau.

const Map: React.FC<{}> = () => {};

google.maps.Map cần có Element làm tham số hàm dựng, nên bạn cần dùng useRef để duy trì một đối tượng có thể thay đổi và sẽ tồn tại trong suốt thời gian hoạt động của thành phần. Đoạn mã sau sẽ tạo bản đồ trong hook useEffect trong phần nội dung của thành phần Map.

TypeScript

const ref = React.useRef<HTMLDivElement>(null);
const [map, setMap] = React.useState<google.maps.Map>();

React.useEffect(() => {
  if (ref.current && !map) {
    setMap(new window.google.maps.Map(ref.current, {}));
  }
}, [ref, map]);

JavaScript

const ref = React.useRef(null);
const [map, setMap] = React.useState();

React.useEffect(() => {
  if (ref.current && !map) {
    setMap(new window.google.maps.Map(ref.current, {}));
  }
}, [ref, map]);

Móc useEffect ở trên sẽ chỉ chạy khi ref thay đổi. Thành phần Map hiện trả về nội dung sau.

return <div ref={ref} />

Mở rộng thành phần bản đồ bằng các đạo cụ khác

Bạn có thể mở rộng thành phần bản đồ cơ bản bằng cách sử dụng các đề xuất khác cho các tuỳ chọn bản đồ, trình nghe sự kiện và kiểu được áp dụng cho div chứa bản đồ. Đoạn mã sau đây cho thấy giao diện mở rộng của thành phần chức năng này.

interface MapProps extends google.maps.MapOptions {
  style: { [key: string]: string };
  onClick?: (e: google.maps.MapMouseEvent) => void;
  onIdle?: (map: google.maps.Map) => void;
}

const Map: React.FC<MapProps> = ({
  onClick,
  onIdle,
  children,
  style,
  ...options
}) => {}

Đối tượng style có thể chuyển trực tiếp qua và đặt làm đạo cụ trên div được hiển thị.

return <div ref={ref} style={style} />;

onClick, onIdlegoogle.maps.MapOptions yêu cầu hook useEffect để áp dụng các nội dung cập nhật bắt buộc cho google.maps.Map.

TypeScript

// because React does not do deep comparisons, a custom hook is used
// see discussion in https://github.com/googlemaps/js-samples/issues/946
useDeepCompareEffectForMaps(() => {
  if (map) {
    map.setOptions(options);
  }
}, [map, options]);

JavaScript

// because React does not do deep comparisons, a custom hook is used
// see discussion in https://github.com/googlemaps/js-samples/issues/946
useDeepCompareEffectForMaps(() => {
  if (map) {
    map.setOptions(options);
  }
}, [map, options]);

Trình nghe sự kiện yêu cầu mã phức tạp hơn một chút để xóa các trình nghe hiện có khi một trình xử lý chuyển qua làm đạo cụ đã được cập nhật.

TypeScript

React.useEffect(() => {
  if (map) {
    ["click", "idle"].forEach((eventName) =>
      google.maps.event.clearListeners(map, eventName)
    );

    if (onClick) {
      map.addListener("click", onClick);
    }

    if (onIdle) {
      map.addListener("idle", () => onIdle(map));
    }
  }
}, [map, onClick, onIdle]);

JavaScript

React.useEffect(() => {
  if (map) {
    ["click", "idle"].forEach((eventName) =>
      google.maps.event.clearListeners(map, eventName)
    );
    if (onClick) {
      map.addListener("click", onClick);
    }

    if (onIdle) {
      map.addListener("idle", () => onIdle(map));
    }
  }
}, [map, onClick, onIdle]);

Tạo thành phần điểm đánh dấu

Thành phần đánh dấu sử dụng các mẫu tương tự như thành phần bản đồ với các móc useEffectuseState.

TypeScript

const Marker: React.FC<google.maps.MarkerOptions> = (options) => {
  const [marker, setMarker] = React.useState<google.maps.Marker>();

  React.useEffect(() => {
    if (!marker) {
      setMarker(new google.maps.Marker());
    }

    // remove marker from map on unmount
    return () => {
      if (marker) {
        marker.setMap(null);
      }
    };
  }, [marker]);

  React.useEffect(() => {
    if (marker) {
      marker.setOptions(options);
    }
  }, [marker, options]);

  return null;
};

JavaScript

const Marker = (options) => {
  const [marker, setMarker] = React.useState();

  React.useEffect(() => {
    if (!marker) {
      setMarker(new google.maps.Marker());
    }

    // remove marker from map on unmount
    return () => {
      if (marker) {
        marker.setMap(null);
      }
    };
  }, [marker]);
  React.useEffect(() => {
    if (marker) {
      marker.setOptions(options);
    }
  }, [marker, options]);
  return null;
};

Thành phần này trả về giá trị rỗng khi google.maps.Map quản lý thao tác DOM.

Thêm điểm đánh dấu làm thành phần con của bản đồ

Để thêm điểm đánh dấu vào bản đồ, thành phần Marker sẽ được chuyển tới thành phần Map bằng cách sử dụng đề xuất children đặc biệt như sau.

<Wrapper apiKey={"YOUR_API_KEY"}>
  <Map center={center} zoom={zoom}>
    <Marker position={position} />
  </Map>
</Wrapper>

Bạn phải thực hiện một thay đổi nhỏ đối với đầu ra của thành phần Map để truyền đối tượng google.maps.Map cho tất cả phần tử con dưới dạng đạo cụ bổ sung.

TypeScript

return (
  <>
    <div ref={ref} style={style} />
    {React.Children.map(children, (child) => {
      if (React.isValidElement(child)) {
        // set the map prop on the child component
        // @ts-ignore
        return React.cloneElement(child, { map });
      }
    })}
  </>
);

JavaScript

return (
  <>
    <div ref={ref} style={style} />
    {React.Children.map(children, (child) => {
      if (React.isValidElement(child)) {
        // set the map prop on the child component
        // @ts-ignore
        return React.cloneElement(child, { map });
      }
    })}
  </>
);

Liên kết bản đồ và trạng thái ứng dụng

Khi sử dụng mẫu ở trên cho các lệnh gọi lại onClickonIdle, bạn có thể mở rộng ứng dụng để tích hợp đầy đủ các thao tác của người dùng như nhấp hoặc xoay bản đồ.

TypeScript

const [clicks, setClicks] = React.useState<google.maps.LatLng[]>([]);
const [zoom, setZoom] = React.useState(3); // initial zoom
const [center, setCenter] = React.useState<google.maps.LatLngLiteral>({
  lat: 0,
  lng: 0,
});

const onClick = (e: google.maps.MapMouseEvent) => {
  // avoid directly mutating state
  setClicks([...clicks, e.latLng!]);
};

const onIdle = (m: google.maps.Map) => {
  console.log("onIdle");
  setZoom(m.getZoom()!);
  setCenter(m.getCenter()!.toJSON());
};

JavaScript

const [clicks, setClicks] = React.useState([]);
const [zoom, setZoom] = React.useState(3); // initial zoom
const [center, setCenter] = React.useState({
  lat: 0,
  lng: 0,
});

const onClick = (e) => {
  // avoid directly mutating state
  setClicks([...clicks, e.latLng]);
};

const onIdle = (m) => {
  console.log("onIdle");
  setZoom(m.getZoom());
  setCenter(m.getCenter().toJSON());
};

Các móc này có thể được tích hợp vào các thành phần biểu mẫu bằng mẫu sau, như được minh họa cho đầu vào vĩ độ.

<label htmlFor="lat">Latitude</label>
<input
  type="number"
  id="lat"
  name="lat"
  value={center.lat}
  onChange={(event) =>
    setCenter({ ...center, lat: Number(event.target.value) })
  }
/>

Cuối cùng, ứng dụng theo dõi số lượt nhấp và hiển thị các điểm đánh dấu ở mỗi vị trí lượt nhấp. Mã sau đây sử dụng biến position để chuyển giá trị LatLng từ sự kiện nhấp.

{clicks.map((latLng, i) => (<Marker key={i} position={latLng} />))}

Khám phá mã

Bạn có thể khám phá mã mẫu hoàn chỉnh thông qua các sân chơi mã trực tuyến bên dưới hoặc bằng cách sao chép kho lưu trữ git.

Thử mẫu

Mẫu nhân bản

Bạn phải có Git và Node.js để chạy mẫu này trên thiết bị. Làm theo những hướng dẫn này để cài đặt Node.js và NPM. Các lệnh sau đây nhân bản, cài đặt các phần phụ thuộc và khởi động ứng dụng mẫu.

  git clone -b sample-react-map https://github.com/googlemaps/js-samples.git
  cd js-samples
  npm i
  npm start

Bạn có thể thử các mẫu khác bằng cách chuyển sang bất kỳ nhánh nào bắt đầu bằng sample-SAMPLE_NAME.

  git checkout sample-SAMPLE_NAME
  npm i
  npm start