Using React
To quickly spin up a React project with the essentials, click here to fork the Mappedin React Template on CodeSandbox. This template already contains the useVenue and useMapView hooks.
For building locally, start by adding initializing a React app and adding @mappedin/mappedin-js.
npx create-react-app mappedin-react-app --template typescript
cd mappedin-react-app
yarn add @mappedin/mappedin-js
The Mappedin Web SDK does not provide React components. You will need to use effects to fetch the data and display it within your app. There are many ways you could achieve this depending on your project setup, but in this guide we'll illustrate writing a few custom React hooks.
useVenue
Create a new hook called useVenue
. The useVenue hook asynchronously fetches the Mappedin data using getVenue. It takes the venue options as a parameter and returns a Mappedin venue object which we pass to useMapView
.
function useVenue(options: TGetVenueOptions) {
// Store the venue object in a state variable
const [venue, setVenue] = useState<Mappedin | undefined>();
// Fetch data asynchronously whenever options are changed
useEffect(() => {
let ignore = false;
const fetchData = async () => {
try {
const data = await getVenue(options);
// Update state variable after data is fetched
if (!ignore) {
setVenue(data);
}
} catch (e) {
// Handle error
console.log(e);
setVenue(undefined);
}
};
fetchData();
return () => {
ignore = true;
};
}, [options]);
// Return the venue object
return venue;
}
useMapView
Create another hook called useMapView
. The useMapView hook calls showVenue to render the MapView on an element. It takes an HTMLElement and the venue object returned from useVenue
as required parameters. It returns the MapView instance which you can use to manipulate the map.
You can read about the other options you can pass to useMapView in the TShowVenueOptions section of the API Reference.
function useMapView(el: HTMLElement | null, venue: Mappedin | undefined, options?: TShowVenueOptions) {
// Store the MapView instance in a state variable
const [mapView, setMapView] = useState<MapView | undefined>();
// Render the MapView asynchronously
useEffect(() => {
const renderVenue = async () => {
// Do nothing if the map container or venue data are not initialized
if (el == null || venue == null) {
return;
}
// Do nothing if the mapView is already rendered with the current venue data
if (mapView != null && mapView.venue.venue.id === venue.venue.id) {
return;
}
// If the mapView has been rendered with old data, destroy it
if (mapView != null) {
mapView.destroy();
}
// Try to render the mapView
try {
const _mapView = await showVenue(el, venue, options);
setMapView(_mapView);
} catch (e) {
// Handle error
console.log(e);
setMapView(undefined);
}
};
renderVenue();
}, [el, venue, options, mapView]);
// Return the MapView instance
return mapView;
}
Result
Using these two custom hooks we can attach a MapView instance to an element in our React component.
// See Trial API key Terms and Conditions
// https://developer.mappedin.com/docs/demo-keys-and-maps
const options: TGetVenueOptions = {
venue: 'mappedin-demo-mall',
clientId: '5eab30aa91b055001a68e996',
clientSecret: 'RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1',
};
export default function App() {
const mapRef = useRef<HTMLDivElement | null>(null);
const venue = useVenue(options);
const mapView = useMapView(mapRef.current, venue);
return <div id="app" ref={mapRef} />;
}
You should now see a rendering of the Mappedin Demo Mall in your React app, like the CodeSandbox example below.
Other Useful Hooks
The above hooks should help you get the basic MapView instance up and running, which you can then use to add interactivity or floating labels to your map. Depending on your needs, you may wish to create additional hooks to handle these map interactions as well.
We've provided code snippets for some supplementary hooks that can help to get you started.
useMapClick
useMapClick
subscribes to the E_SDK_EVENT.CLICK event. It will fire the passed onClick function when the MapView detects a click event.
function useMapClick(mapView: MapView | undefined, onClick: (payload: E_SDK_EVENT_PAYLOAD[E_SDK_EVENT.CLICK]) => void) {
const handleClick = useCallback(
(payload: E_SDK_EVENT_PAYLOAD[E_SDK_EVENT.CLICK]) => {
onClick(payload);
},
[onClick],
);
// Subscribe to E_SDK_EVENT.CLICK
useEffect(() => {
if (mapView == null) {
return;
}
mapView.on(E_SDK_EVENT.CLICK, handleClick);
// Cleanup
return () => {
mapView.off(E_SDK_EVENT.CLICK, handleClick);
};
}, [mapView, handleClick]);
}
You can read more about click events in the Adding Interactivity guide.
useOfflineSearch
useOfflineSearch
creates or reuses an OfflineSearch instance and passes it a new query all within one hook. It uses a new hook introduced in React 18, useDeferredValue, to improve performance by ensuring state updates are completed before performing another search.
function useOfflineSearch(venue: Mappedin | undefined, query: string) {
// Store the OfflineSearch instance in a state variable
const [searchInstance, setSearchInstance] = useState<OfflineSearch | undefined>();
// Store the most recent results
const [results, setResults] = useState<TMappedinOfflineSearchResult[]>([]);
// Defer the new search query until state updates are complete
const deferredQuery = useDeferredValue(query);
// Create the OfflineSearch instance
useEffect(() => {
if (venue == null) {
setSearchInstance(undefined);
return;
}
const instance = new OfflineSearch(venue);
setSearchInstance(instance);
}, [venue]);
// Get search results asynchronously
useEffect(() => {
if (venue == null || searchInstance == null || deferredQuery === '') {
setResults([]);
return;
}
const generateSearchResults = async () => {
const results = await searchInstance.search(deferredQuery);
setResults(results);
};
generateSearchResults();
}, [deferredQuery, venue, searchInstance]);
// Return the most recent results
return results;
}
When using the useOfflineSearch hook, be sure to memoize the results to apply the useDeferredValue optimization to your component.
const results = useOfflineSearch(venue, searchQuery);
const searchResults = useMemo(
() =>
results
.filter((result) => result.type === 'MappedinLocation')
.map((result) => (
<div
id="search-result"
key={(result.object as MappedinLocation).name}
onClick={() => {
setSelectedLocation(result.object as MappedinLocation);
setSearchQuery('');
}}
>
{`${result.object.name}`}
</div>
)),
[results],
);
You can read more about Mappedin's built-in search in the Search guide.
Result
With additional hooks in place, we can build a performant interactive map experience in our React app. Try clicking on or searching for a location in the sandbox below, or browse the code to see how we put it all together.