I am building an app with React, TS, Mapbox, react-mapbox-gl and RecoilJS.
This is the Dashboard
component in which I'm working:
import React from "react";
import PageTemplate from "../../components/PageTemplate/PageTemplate";
import ReactMapboxGl from "react-mapbox-gl";
import { CGOIncidentsMapConfig } from "./Map/CGOIncidents";
import { zoomSuscriberState, clickSuscriberState } from "../../contexts/MapContext";
import { useRecoilValue } from "recoil";
import { MapEvent } from "react-mapbox-gl/lib/map-events";
const Map = ReactMapboxGl({
accessToken: process.env.REACT_APP_MAPBOX_TOKEN ?? "",
});
const getEventHandle: (subscribers: MapEvent[]) => MapEvent = (subscribers) => (map, evt) => {
console.log(subscribers);
for (const event of subscribers) {
event(map, evt);
}
};
const Dashboard: React.FC = () => {
const zoomSuscribers = useRecoilValue(zoomSuscriberState);
const clickSuscribers = useRecoilValue(clickSuscriberState);
console.log(zoomSuscribers);
console.log(clickSuscribers);
return (
<PageTemplate>
<Map
style="mapbox://styles/mapbox/light-v10"
containerStyle={{
height: "100%",
width: "100%",
}}
center={[-3.7, 40.44]}
zoom={[5]}
onZoom={getEventHandle(zoomSuscribers)}
onClick={getEventHandle(clickSuscribers)}
>
<CGOIncidentsMapConfig />
</Map>
</PageTemplate>
);
};
export default Dashboard;
This is the CGOIncidentsMapConfig
component simplified:
export const CGOIncidentsMapConfig: React.FC = () => {
const [popupCoordinates, setPopupCoordinates] = useState<number[]>([-0.13235092163085938, 51.518250335096376]);
const [isPopupVisible, setPopupVisible] = useState<boolean>(false);
const [popopContainer, setPopupContainer] = useState("");
const [leyend, setLeyend] = useState<{
title: string;
elements: {
title: string;
color: string;
}[];
}>(LEYENDS.PROVINCES);
const onZoomMapEvent: MapEvent = (map) => {
console.log("ZIIIM", map.getZoom());
};
const onMouseEnterEvent = (e: MapLayerMouseEvent) => {
const map = e.target;
map.getCanvas().style.cursor = "pointer";
};
const onMouseLeaveEvent = (e: MapLayerMouseEvent) => {
const map = e.target;
map.getCanvas().style.cursor = "";
};
const dismissPopup = () => {
setPopupVisible(false);
console.log("CIIM");
};
const setZoomSuscribers = useSetRecoilState(zoomSuscriberState);
const setClickSuscribers = useSetRecoilState(clickSuscriberState);
useEffect(() => {
setZoomSuscribers((oldZoomSubscribers) => [...oldZoomSubscribers, onZoomMapEvent]);
setClickSuscribers((oldClickSubscribers) => [...oldClickSubscribers, dismissPopup]);
console.log("updated recoil");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
console.log(isPopupVisible);
return (
<Fragment>
<Source id={CGO_INCIDENTS_SOURCE_ID} tileJsonSource={CGO_INCIDENTS_SOURCE_OPTIONS} />
<Layer
id={CGO_INCIDENTS_LAYER_LAYOUT.id}
type={CGO_INCIDENTS_LAYER_LAYOUT.type}
sourceId={CGO_INCIDENTS_LAYER_LAYOUT.source}
sourceLayer={CGO_INCIDENTS_LAYER_LAYOUT["source-layer"]}
paint={CGO_INCIDENTS_LAYER_LAYOUT.paint}
onClick={onClickEvent}
onMouseEnter={onMouseEnterEvent}
onMouseLeave={onMouseLeaveEvent}
/>
{isPopupVisible && (
<Popup coordinates={popupCoordinates} onClick={dismissPopup}>
{popopContainer}
</Popup>
)}
<Leyend title={leyend.title} elements={leyend.elements} />
</Fragment>
);
};
This is my MapContext
file in which I created the atoms:
import { MapEvent } from "react-mapbox-gl/lib/map-events";
import { atom } from "recoil";
export const zoomSuscriberState = atom<MapEvent[]>({ key: "zoomSuscriberState", default: [] });
export const clickSuscriberState = atom<MapEvent[]>({ key: "clickSuscriberState", default: [] });
As you can see, what I want to do is just to create a subscriber pattern for the onClick and onZoom events with RecoilJS. My idea with this is to separate each Mapbox layer in different components.
When the function returned by getEventHandler
is executed, the console.log inside only displays an empty array for zoomSuscribers
or clickSuscribers
but what I see with the console.log into the Dashboard
component is that both atoms have a callback function settled by the CGOIncidentsMapConfig
component. These atoms are empty at the first render of Dashboard
and fulfilled once CGOIncidentsMapConfig
component has been rendered. Seems like RecoilJS updated both atoms and the Dashboard
component, as expected.
Why the console.log inside the getEventHandler
functions only prints an empty array? Therefore, the event are not properly executed by the subscribers.
Thanks in advance.