Select to view content in your preferred language

Trouble Using Reactive Utils On Initially Undefined Variable

260
9
Jump to solution
Tuesday
JasonBartling1
Regular Contributor

I have a global variable for highlight handles that I'm trying to watch to update a label with the number of currently selected features.  However, the variable is initially undefined and this seems to be causing an issue.  Even if I set the watch options to initial is true, the code will only execute the first time.  Is there a way for me to run it initially and then continue to monitor once the variable is defined? This is my code:  

reactiveUtils.watch(()=> globalThis.highlightHandles?._groups.size, () =>{

  const numSelected = globalThis.highlightHandles?._groups.size;
  selectedCount.innerText = numSelected;
  console.log("Num selected is "+numSelected);

  if (numSelected){
    selectedCount.style.color = 'black';
    moreActionsButton.disabled = false;
  }
  else {

    selectedCount.innerText = "0";
    selectedCount.style.color = '#c9c9c9';
    moreActionsButton.disabled = true;
  }

},{initial: true});

Any help would be greatly appreciated!

0 Kudos
1 Solution

Accepted Solutions
ReneRubalcava
Esri Frequent Contributor

The reactiveUtils only work against the SDK classes, not native objects and arrays, so we can't watch for changes on `globalThis`. The root object in the watchExpression needs to be an SDK class.

View solution in original post

9 Replies
MatthewDriscoll
MVP Alum

Try using a when before the watch.  Might want to use a {once: true} for the when.

reactiveUtils.when(
  () => globalThis.highlightHandles != null,
  () => {

    reactiveUtils.watch(
      () => globalThis.highlightHandles._groups.size,

 

0 Kudos
JasonBartling1
Regular Contributor

Unfortunately, that does not seem to work.  This is the updated code: 

reactiveUtils.when(()=> globalThis.highlightHandles != null, ()=>{
  reactiveUtils.watch(()=> globalThis.highlightHandles._groups.size, () =>{

    const numSelected = globalThis.highlightHandles._groups.size;
    selectedCount.innerText = numSelected;
    console.log("Num selected is "+numSelected);

    if (numSelected){
      selectedCount.style.color = 'black';
      moreActionsButton.disabled = false;
    }
    else {

      selectedCount.innerText = "0";
      selectedCount.style.color = '#c9c9c9';
      moreActionsButton.disabled = true;
    }

  });
});
0 Kudos
MatthewDriscoll
MVP Alum

Try creating the variable like this instead.  Also _groups would make it private, so the API might not see it as observable.

reactiveUtils.when(
  () => globalThis.highlightHandles != null,
  () => {

    reactiveUtils.watch(
      () => globalThis.highlightHandles._groups.size,
      (numSelected) => {...

 

0 Kudos
JeffreyThompson2
MVP Frequent Contributor

This is one of those issues I usually slap a setTimeout() on.

setTimeout(()=> {  
reactiveUtils.watch(()=> globalThis.highlightHandles._groups.size, () =>{

    const numSelected = globalThis.highlightHandles._groups.size;
    selectedCount.innerText = numSelected;
    console.log("Num selected is "+numSelected);

    if (numSelected){
      selectedCount.style.color = 'black';
      moreActionsButton.disabled = false;
    }
    else {

      selectedCount.innerText = "0";
      selectedCount.style.color = '#c9c9c9';
      moreActionsButton.disabled = true;
    }

  });
}, 100)

It's not a best practice, but it usually works. Try dialing up or down the wait a few times and see if it works.

GIS Developer
City of Arlington, Texas
0 Kudos
ReneRubalcava
Esri Frequent Contributor

The reactiveUtils only work against the SDK classes, not native objects and arrays, so we can't watch for changes on `globalThis`. The root object in the watchExpression needs to be an SDK class.

JasonBartling1
Regular Contributor

Thanks Rene.  I did not realize it could only be used to track changes to the SDK objects.  Since I am also using a feature table to highlight, I ended up just watching the featureTable.highlightIds.length property.  This code worked for me:  

reactiveUtils.watch(()=> featureTable.highlightIds.length, (numSelected) =>{

  selectedCount.innerText = numSelected;

  if (numSelected > 0){

    selectedCount.style.color = 'black';
    moreActionsButton.disabled = false;
  }
  else {

    selectedCount.style.color = '#c9c9c9';
    moreActionsButton.disabled = true;
    
  }

});
MatthewDriscoll
MVP Alum

Try this based off of @JeffreyThompson2 suggestion and what we have learned from @ReneRubalcava .  

 setInterval(() => {

  const numSelected = globalThis.highlightHandles?._groups?.size || 0;

  selectedCount.innerText = numSelected;

  console.log("Num selected is " + numSelected);

  if (numSelected) {
    selectedCount.style.color = 'black';
    moreActionsButton.disabled = false;
  } else {
    selectedCount.style.color = '#c9c9c9';
    moreActionsButton.disabled = true;
  }

}, 250);
RafelMayol
New Contributor

You’ll probably need to watch the object after it’s assigned, because watch() can’t keep tracking an undefined reference reliably. A cleaner approach is to watch the property that changes, or re-register the watcher once highlightHandles gets initialized. The initial: true option only fires once with the current value — it doesn’t wait for future definition changes.

0 Kudos
RafelMayol
New Contributor

@FCTV33 You’ll probably need to watch the object after it’s assigned, because watch() can’t keep tracking an undefined reference reliably. A cleaner approach is to watch the property that changes, or re-register the watcher once highlightHandles gets initialized. The initial: true option only fires once with the current value — it doesn’t wait for future definition changes.

0 Kudos