סקירה כללית
במדריך הזה תלמדו איך להוסיף מפה וסמני מיקום לאפליקציית React באמצעות @googlemaps/react-wrapper ולשלב את המפה והסמנים במצב האפליקציה.
התקנה של @googlemaps/react-wrapper
צריך להתקין את הספרייה @googlemaps/react-wrapper ולהשתמש בה כדי לטעון באופן דינמי את ה-API של מפות Google ל-JavaScript כאשר הרכיב נמצא בעיבוד.
npm install @googlemaps/react-wrapper
ניתן לייבא את הספרייה הזו ולהשתמש בה עם הפריטים הבאים.
import { Wrapper, Status } from "@googlemaps/react-wrapper";
השימוש הבסיסי ברכיב הזה הוא לשלב בין רכיבי צאצא שתלויים ב-JavaScript JavaScript API. הרכיב Wrapper
מקבל גם רכיב render
לעיבוד רכיבים בטעינה או לטיפול בשגיאות בטעינת ה-API של מפות JavaScript.
const render = (status: Status) => {
return <h1>{status}</h1>;
};
<Wrapper apiKey={"YOUR_API_KEY"} render={render}>
<YourComponent/>
</Wrapper>
הוספת רכיב מפה
סביר להניח שרכיב פונקציונלי בסיסי לעיבוד מפה ישתמש ב-useRef
, useState
ו-useEffect
תגובות webhook.
לרכיב המפה ההתחלתית תהיה החתימה הבאה.
const Map: React.FC<{}> = () => {};
מאחר שהפונקציה google.maps.Map
מחייבת Element
בתור פרמטר בונה, יש להשתמש ב-useRef כדי לשמור על אובייקט בר שינוי שיימשך כל משך החיים של הרכיב. קטע הקוד הבא יוצר מפה בתוך הוו useImpact בגוף הרכיב 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} />
הרחבת רכיב המפה עם אביזרים נוספים
ניתן להרחיב את רכיב המפה הבסיסי באמצעות אביזרים נוספים עבור אפשרויות מפה, פונקציות מסוג event event וסגנונות שונים של מידע שחלים על ה-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 של אירוע מחייבות קוד מורכב יותר כדי לנקות פונקציות האזנה קיימות כאשר handler עובר באופן ידני כאביזר.
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; };
הרכיב מחזיר ערך ריק כאשר 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} />))}
קוד Explore
אפשר לחקור את הקוד המלא באמצעות גני המשחקים של הקוד שבהמשך או על ידי שכפול מאגר ה-git.
לדגום
שכפול הדגימה
Git ו-Node.js נדרשים כדי להריץ את הדוגמה הזו באופן מקומי. כדי להתקין את Node.js ו-NPM, פועלים לפי ההוראות האלה. הפקודות הבאות משכפלות, מתקינים יחסי תלות ומפעילים את האפליקציה לדוגמה.
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