ภาพรวม
บทแนะนํานี้จะแสดงวิธีเพิ่มแผนที่และเครื่องหมายลงในแอปพลิเคชันแสดงความรู้สึกโดยใช้ @googlemaps/react-wrapper และผสานรวมแผนที่และเครื่องหมายไว้ในสถานะแอปพลิเคชัน
ติดตั้ง @googlemaps/react-wrapper
ติดตั้งและใช้ไลบรารี @googlemaps/react-wrapper เพื่อโหลด Maps JavaScript API แบบไดนามิกเมื่อคอมโพเนนต์แสดงผล
npm install @googlemaps/react-wrapper
คุณสามารถนําเข้าและใช้ไลบรารีนี้ร่วมกับรายการต่อไปนี้
import { Wrapper, Status } from "@googlemaps/react-wrapper";
การใช้งานพื้นฐานของคอมโพเนนต์นี้คือการรวมองค์ประกอบย่อยที่ใช้ Maps JavaScript API และคอมโพเนนต์ Wrapper
ยังยอมรับคอมโพเนนต์ render
สําหรับการแสดงผลคอมโพเนนต์การโหลดหรือการจัดการข้อผิดพลาดขณะโหลด Maps JavaScript API
const render = (status: Status) => {
return <h1>{status}</h1>;
};
<Wrapper apiKey={"YOUR_API_KEY"} render={render}>
<YourComponent/>
</Wrapper>
เพิ่มคอมโพเนนต์แผนที่
คอมโพเนนต์ฟังก์ชันพื้นฐานที่จะแสดงแผนที่มีแนวโน้มที่จะใช้ฮุก React ของ useRef
, useState
และ useEffect
คอมโพเนนต์แผนที่เริ่มต้นจะมีลายเซ็นต่อไปนี้
const Map: React.FC<{}> = () => {};
เนื่องจาก google.maps.Map
ต้องใช้ Element
เป็นพารามิเตอร์ตัวสร้าง จึงจําเป็นต้องมี useRef เพื่อรักษาออบเจ็กต์ที่เปลี่ยนแปลงได้ซึ่งจะคงอยู่ตลอดอายุการใช้งานของคอมโพเนนต์ ข้อมูลโค้ดต่อไปนี้จะสร้างอินสแตนซ์ของแผนที่ในเว็บฮุค useEffect ในเนื้อความของคอมโพเนนต์ 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]);
ฮุก useEffect
ข้างต้นจะทํางานเมื่อ ref
มีการเปลี่ยนแปลงเท่านั้น ตอนนี้คอมโพเนนต์ Map
จะแสดงผลสิ่งต่อไปนี้
return <div ref={ref} />
ขยายคอมโพเนนต์แผนที่ด้วยอุปกรณ์เพิ่มเติม
คอมโพเนนต์แผนที่พื้นฐานสามารถขยายตัวได้ด้วยตัวประกอบเพิ่มเติมสําหรับตัวเลือกแผนที่ Listener เหตุการณ์ และสไตล์ที่ใช้กับ div ที่มีแผนที่ โค้ดต่อไปนี้แสดงอินเทอร์เฟซแบบขยายของคอมโพเนนต์ฟังก์ชันนี้
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
}) => {}
ออบเจ็กต์ style
สามารถส่งผ่านโดยตรงและตั้งค่าเป็นฉากในการแสดงผล div
return <div ref={ref} style={style} />;
onClick
, onIdle
และ google.maps.MapOptions
ต้องใช้ฮุก useEffect
เพื่อใช้การอัปเดตกับ 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]);
Listener เหตุการณ์จําเป็นต้องมีโค้ดที่ซับซ้อนขึ้นเล็กน้อยเพื่อล้าง Listener ที่มีอยู่เมื่อมีการอัปเดตเครื่องจัดการผ่านอุปกรณ์ประกอบฉาก
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]);
สร้างคอมโพเนนต์ของตัวทําเครื่องหมาย
คอมโพเนนต์ตัวทําเครื่องหมายใช้รูปแบบที่คล้ายกับคอมโพเนนต์แผนที่ซึ่งมีฮุก useEffect
และ useState
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; };
คอมโพเนนต์จะแสดงผลค่า Null เมื่อ google.maps.Map
จัดการการควบคุม DOM
เพิ่มตัวทําเครื่องหมายเป็นคอมโพเนนต์ย่อยของแผนที่
ในการเพิ่มตัวทําเครื่องหมายลงในแผนที่ ระบบจะส่งคอมโพเนนต์ Marker
ไปยังคอมโพเนนต์ Map
โดยใช้ตัวประกอบ children
พิเศษดังต่อไปนี้
<Wrapper apiKey={"YOUR_API_KEY"}>
<Map center={center} zoom={zoom}>
<Marker position={position} />
</Map>
</Wrapper>
ต้องเปลี่ยนแปลงเอาต์พุตเล็กน้อยของคอมโพเนนต์ Map
เพื่อส่งออบเจ็กต์ google.maps.Map
ไปยังเด็กทั้งหมดเป็นอุปกรณ์เพิ่มเติม
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 }); } })} </> );
ลิงก์แผนที่และสถานะแอปพลิเคชัน
เมื่อใช้รูปแบบข้างต้นสําหรับการเรียกกลับ onClick
และ onIdle
แอปพลิเคชันจะสามารถขยายเพื่อรวมการทํางานของผู้ใช้ทั้งหมด เช่น การคลิกหรือเลื่อนแผนที่
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()); };
ตะขอเหล่านี้ผสานรวมกับองค์ประกอบแบบฟอร์มได้โดยใช้รูปแบบต่อไปนี้ตามที่แสดงในอินพุตละติจูด
<label htmlFor="lat">Latitude</label>
<input
type="number"
id="lat"
name="lat"
value={center.lat}
onChange={(event) =>
setCenter({ ...center, lat: Number(event.target.value) })
}
/>
สุดท้าย แอปพลิเคชันจะติดตามคลิก และแสดงเครื่องหมายในตําแหน่งคลิกแต่ละตําแหน่ง โค้ดต่อไปนี้ใช้ตัวแปร position
เพื่อส่งค่า LatLng
จากเหตุการณ์การคลิก
{clicks.map((latLng, i) => (<Marker key={i} position={latLng} />))}
สํารวจโค้ด
โดยโค้ดตัวอย่างที่สมบูรณ์จะสํารวจผ่านสนามเด็กเล่นโค้ดออนไลน์ด้านล่างหรือโคลนที่เก็บ Git ก็ได้
ลองใช้ตัวอย่าง
ตัวอย่างการโคลน
ต้องใช้ Git และ Node.js เพื่อเรียกใช้ตัวอย่างนี้ในเครื่อง ทําตามวิธีการติดตั้ง Node.js และ NPM คําสั่งต่อไปนี้จะโคลน ติดตั้งทรัพยากร Dependency และเริ่มแอปพลิเคชันตัวอย่าง
git clone -b sample-react-map https://github.com/googlemaps/js-samples.git
cd js-samples
npm i
npm start
สามารถลองใช้ตัวอย่างอื่นๆ โดยเปลี่ยนไปใช้สาขาใดก็ได้ที่ขึ้นต้นด้วย sample-SAMPLE_NAME
git checkout sample-SAMPLE_NAME
npm i
npm start