We just upgraded to ArcGIS Enterprise 11.1 and I am playing with custom experience builder widgets.
I have a widget that loads, but only if the map and layers finish loading first. If the user clicks the button too soon the widget errors with a message
“Cannot read properties of null (reading 'view')”
(See screenshot attached).
I figured out that it happens because the app isn’t fully finished loading so the mapView is null.
const viewManager = MapViewManager.getInstance();
console.log(viewManager); // is not null if widget loaded too soon
const mapView = viewManager.getJimuMapViewById(viewManager.getAllJimuMapViewIds()[0]);
console.log(mapView); // returns null if widget loaded too soon
As long as the user waits long enough, mapView will not be null. But if pressed too quickly it is null.
In an Experience Builder React widget, is there a way to only load the widget after the map and layers finish loading? (Kind of like how the Esri JS SDK has the view.when, or something like that). Or is there some other way of getting the same result? That way, even if the user clicks the button too soon to open the widget, it will still load only after the map and layers finish and won’t error.
Thanks!
FYI: I am an extreme newbie with React and Experience Builder custom widgets.
Is it possible to check if the map view is null in the render step of the appropriate component and return a placeholder fragment if it's null instead of the proper widget fragment? Haven't played with custom widgets much but if the view manager is passed into the component's props then React should handle the rest.
Unfortunately, I’m an extreme newbie with React and experience builder widgets, so I don’t understand your thoughts.
Here is the beginning of my widget. Are you saying the mapView should be in the props of Widget somehow? Like next to the "AllWidgetProps<any>"
const Widget = (props: AllWidgetProps<any>) => {
const viewManager = MapViewManager.getInstance();
console.log(viewManager); // is not null if widget loaded too soon
const mapView = viewManager.getJimuMapViewById(viewManager.getAllJimuMapViewIds()[0]);
console.log(mapView); // returns null if widget loaded too soon
return (
<div></div>
);
}
or where the
I think the next best step is to look over this sample widget and see if you can adapt this pattern to a functional component. The React Reference should have examples of all hooks in class and function formats.
useEffect(() => {
if (jimuMapView) {
reactiveUtils
.whenOnce(() => jimuMapView.view.ready)
.then(() => {
setMapReady(true)
}
)
}
}, [jimuMapView])
Almost all of my widgets contain a block of code like this. This useEffect function fires everytime the value of jimuMapView changes. If jimuMapView is truthy, it use the reactiveUtils from the API to wait for the mapView to be ready and that sets the value of my mapReady piece of state to true. All of my functional parts of my widget are usually down a level on the React tree waiting for mapReady to be true before loading.