I am building a dashboard filled with Esri maps that are editable. The structure of the components is something like this:
Try adding the graphic
state to the useEffect
dependency array [graphic]
useEffect(function, [graphic])
or useEffect(function, [props])
but props may cause more re-renders than you may want
A more complete example would look like this.
const [graphic, setGraphic] = useState(null);
useEffect(() => {
loadModules(['esri/Graphic']).then(([Graphic]) => {
// Parse out the Lat-Long from each Event and the doc_count
for (let i = 0; i < data?.events.length; i++) {
const point = {
type: "point", // autocasts as new Point
longitude: data?.events[i]?.location.split(",")[1],
latitude: data?.events[i]?.location.split(",")[0]
};
// Create a symbol for rendering the graphic
const symbol = {
type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
style: "circle", color: color, // Color Selected on popup
size: "12px", outline: {
color: [255, 255, 255], // White
width: 1.5
}
};
// Create attributes for popup
const attributes = {
watcherType: humanize(data?.events[i]?.doc_fields?.watcher_type),
eventCount: data?.events[i]?.doc_count,
plural: pluralize(data?.events[i]?.doc_count, 'Event'),
deviceName: data?.events[i]?.key,
lat: data?.events[i]?.location.split(",")[0],
long: data?.events[i]?.location.split(",")[1],
account: data?.events[i]?.doc_fields?.account_id,
address: data?.events[i]?.doc_fields['@service_address'],
meterId: data?.events[i]?.doc_fields?.meter_id,
lastEvent: getFormattedDate(data?.events[i]?.doc_fields['@time_raised_last'], '')
};
// Create popup template
const popupTemplate = {
title: "{eventCount} {watcherType} {plural}",
content:
"<ul><li><b>Address:</b> {address}</li>" +
"<li><b>Account ID:</b> {account}</li>" +
"<li><b>Meter ID:</b> {meterId}</li>" +
"<li><b>Last Event:</b> {lastEvent}</li>" +
"<li><a href='https://maps.google.com/maps?q=&layer=c&cbll={lat},{long}&cbp='>Google Street View</a></li></ul>"
};
// Add the multiPoints to a new graphic
const graphic = new Graphic({
geometry: point,
symbol: symbol,
attributes: attributes,
popupTemplate: popupTemplate
});
setGraphic(graphic);
props.view.graphics.add(graphic);
}
}).catch((err) => console.error(err));
return function cleanup() {
props.view.graphics.remove(graphic);
};
}, [graphic]);
or using props,
const [graphic, setGraphic] = useState(null);
useEffect(() => {
loadModules(['esri/Graphic']).then(([Graphic]) => {
// Parse out the Lat-Long from each Event and the doc_count
for (let i = 0; i < data?.events.length; i++) {
const point = {
type: "point", // autocasts as new Point
longitude: data?.events[i]?.location.split(",")[1],
latitude: data?.events[i]?.location.split(",")[0]
};
// Create a symbol for rendering the graphic
const symbol = {
type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
style: "circle", color: color, // Color Selected on popup
size: "12px", outline: {
color: [255, 255, 255], // White
width: 1.5
}
};
// Create attributes for popup
const attributes = {
watcherType: humanize(data?.events[i]?.doc_fields?.watcher_type),
eventCount: data?.events[i]?.doc_count,
plural: pluralize(data?.events[i]?.doc_count, 'Event'),
deviceName: data?.events[i]?.key,
lat: data?.events[i]?.location.split(",")[0],
long: data?.events[i]?.location.split(",")[1],
account: data?.events[i]?.doc_fields?.account_id,
address: data?.events[i]?.doc_fields['@service_address'],
meterId: data?.events[i]?.doc_fields?.meter_id,
lastEvent: getFormattedDate(data?.events[i]?.doc_fields['@time_raised_last'], '')
};
// Create popup template
const popupTemplate = {
title: "{eventCount} {watcherType} {plural}",
content:
"<ul><li><b>Address:</b> {address}</li>" +
"<li><b>Account ID:</b> {account}</li>" +
"<li><b>Meter ID:</b> {meterId}</li>" +
"<li><b>Last Event:</b> {lastEvent}</li>" +
"<li><a href='https://maps.google.com/maps?q=&layer=c&cbll={lat},{long}&cbp='>Google Street View</a></li></ul>"
};
// Add the multiPoints to a new graphic
const graphic = new Graphic({
geometry: point,
symbol: symbol,
attributes: attributes,
popupTemplate: popupTemplate
});
setGraphic(graphic);
props.view.graphics.add(graphic);
}
}).catch((err) => console.error(err));
return function cleanup() {
props.view.graphics.remove(graphic);
};
}, [props]);
I would think just using the graphic
state would be closer to what you are looking for. Leaving the dependency array empty causes the useEffect
hook to only fire on the initial component mount.
Adding the graphic
state to the array tells the useEffect
to watch for changes in the graphic
state, and if it changes, refire the useEffect
again
This post may be helpful Hooks and Dependency Arrays