Continuing to discover Valtio, we use this library to simplify the code of a React component which renders a map centred on your current location (you need to enable geolocation for the snippet above to work).
The geolocation API describes how to get the current location. Since this is an async call, you would use useEffect
with dependencies. It is a bit tricky to set up. The Valtio library makes it easy: just use proxy
, useSnapshot
and derive
for async callbacks.
The store and the async callback
We declare a store with two fields, "defaultPos" and "current", then proxy
it, and then derive
the "current" field from the geolocation API.
import { proxy } from 'valtio';
import { derive } from 'valtio/utils';
const defaultPos = { lat: 42.2808, lng: -83.743 };
export const gps = proxy({
initPos: defaultPos,
current: null,
});
derive({
derPos: async (get) => {
if (!navigator.geolocation) return (get(gps).initPos = defaultPos);
navigator.geolocation.getCurrentPosition(success, fail);
function fail(e) { return e }
function success({ coords: { latitude, longitude } }) {
get(gps).current = { lat: latitude, lng: longitude };
}
},
});
The <App />
component
In order for React to render when the position is obtained, we simply snap the desired field of the store with useSnapshot
. We also need to suspend it since it's an async rendering.
import Map from './map';
import React from 'react';
import { useSnapshot } from 'valtio';
import { gps } from './geolocate';
function App() {
const { current } = useSnapshot(gps);
return (
<React.Suspense fallback={<h1>Loading</h1>}>
<div>
{current && <Map coord={current} />}
</div>
</React.Suspense>
);
}
export default App;
The <Map />
component
We use the traditional React code as the Valtio library might not bring an obvious advantage, especially as we need to keep track of a DOM component with useRef
.
import React from "react";
import "./styles.css";
import { MapContainer, TileLayer } from "react-leaflet";
export function Map({ coord: { lat, lng } }) {
return (
<MapContainer center={[lat, lng]} zoom={4} class="leaflet-container">
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
/>
</MapContainer>
);
}
Top comments (0)