Plugin works slow on processing selection of component with instances (weird behavior)

I am developing a plugin and I am doing some conditional logic depending on what is selected. I am using:

figma.currentPage.selection[0]

Now, what I don’t understand is that if I am selecting a component node and this component doesn’t have many instances the code works fast. But when I am selecting a component that has lots of instances the code is very slow. No part of my code analyzes the instances (I made some simplified tests to make sure), so why the processing time is higher when the component has instances? They should not be taken into consideration.

Here is the rest of my code (simplified) for reference:

let selected = figma.currentPage.selection[0]

  if (typeof selected !== "undefined" 
  && selected.type === 'COMPONENT'
  ) {
    console.log(selected.mainComponent.remote)

  } else {
    figma.ui.postMessage({
      scene: 'selectComponent'
    })
  }
}

Any ideas why the processing time is dependent on how many instances the selected component has (and how to prevent it)?

I don’t have an exact answer but

Check out this topic: Figma layers tree traversal + estimating size

And this thread: https://twitter.com/zyumbik/status/1356604734296236032

Thanks, @Gleb, it seems that this is a different issue. I think that your issue was about the hidden children of a parent, while mine is about instances of component that are influencing the performance of processing of that component.

Without seeing what kind of processing you are doing in your code and how you are doing it, it’s hard to say why that could be an issue. The number of instances is unlikely to cause slowness (unless these are two completely identical components of course).

I will record a video.

This may not be relevant, but I saw this in the API which was new to me.

Perhaps your code is being affected by this if you’re modifying the main component?

Sorry I don’t have any good ideas to help diagnose/fix, other than perhaps wrapping the code in an async block so it doesn’t block the main thread…

Here is a video: Instances treated as children in Figma Plugin API - YouTube
If the video is not clear, I can record it with a voice-over tomorrow.

I was doing some tests and the root of the problem lays in iterating on the children of the component. For some reason, Figma takes into consideration the instances of the component as well. It hapends in this line:

if (selected.children.every(child => {
      if (child.type == 'INSTANCE') {
        if ( child.mainComponent.remote === false ) {
          // console.log(child.mainComponent.id, ' is not remote ')
          return true
        }
      }
    })
  ) 

Thanks for your help.

Ok that’s weird. I don’t think this is what’s causing the issue but I wouldn’t advice you to put a function inside of the condition. Better store the result as a constant and use it in the condition. Just a bit of a personal advice.

Also it would be nice to take a look at the whole code or maxToGenerate function (and whatever other functions it depends on).

Thanks @Gleb, here is the code for maxToGenerate():

function returnHowManyCanBeGenerated() {
  let selected = figma.currentPage.selection[0].children
  let allVariants = []
  selected.forEach(instance => {
    if ( instance.mainComponent.parent.type == 'COMPONENT_SET' ) {
      allVariants.push(instance.mainComponent.parent.children.length);
    }
  })
  let numberOfPossibleUniques = allVariants.reduce((a, b) => a * b, 1)
  let numberOfSecondsToGenerate = Math.floor((numberOfPossibleUniques * 68) / 1000)
  // console.log(`number of possible uniques: ` + numberOfPossibleUniques)
  figma.ui.postMessage({
    type: 'numberOfPossibleUniques',
    number: numberOfPossibleUniques,
    time: numberOfSecondsToGenerate
  })
  return numberOfPossibleUniques
}

function maxToGenerate() {
  if (returnHowManyCanBeGenerated() > 10000) return 10000
  else return returnHowManyCanBeGenerated()
}

I was doing tests without this function as well (with a simple “console.log” instead) the time was indeed a bit shorter, but still significantly longer than when processing a component without instances. I will try to start a new project with the simplest code possible to track the root of the problem.

@Gleb here is a video with simplified code:

Here is the code:

UI:

<body style="background: #E0F8E0;">
    <p style="color: green;">The right component was selected and processed. </p>
</body>

<script>
    onmessage = (event) => {
        if (event.data.pluginMessage.scene == 'selectComponent') {
            document.querySelector('body').style.background = "white"
            document.querySelector('body').innerHTML =
            `
            <h3>Select Component</h3>
            ` 
        } else if (document.getElementById('uniquenumber')) {
        } 
    }
</script>

Code:

// @ts-nocheck
returnCurrentSelection()

figma.on("selectionchange", () => {
  returnCurrentSelection()
}) 

function returnCurrentSelection() {
  let selected = figma.currentPage.selection[0]

  figma.showUI(__html__, {
    width: 256,
    height: 400,
    title: "Component problem"
  });

  if (typeof selected !== "undefined" 
  && selected.type === 'COMPONENT'
  && selected.children.every(child => {
      if (child.type == 'INSTANCE') {
        if ( child.mainComponent.remote === false ) {
          return true
        }
      }
    })
  ) { 
    console.log('hello')
  } else {
    figma.ui.postMessage({
      scene: 'selectComponent'
    })
  }
}

I am not a seasoned developer, but it seems to me that the issue is that the instances are treated as children (which in my humble understanding should not be the case?). I triple-checked the documentation to make sure about the distinction of children (something that is inside the nodes tree) and instance (a node that lives on a separate tree).
When I console.log the parent of instances it is the Page node. The same goes when I console log the children of the component. They are just the layers that are in the component itself.

@Gleb, about putting the result of the condition under the constant:
I am not very good with the syntax yet, so I am just trying to “make it work” ;). I was trying to do what you advised, but it doesn’t work:

Just not sure how to do it, so I am leaving it as it was.

I meant this:

if (typeof selected !== "undefined" 
  && selected.type === 'COMPONENT') {
  // save result to constant
  const isRemote = selected.children.every(child => {
      if (child.type == 'INSTANCE') {
        if ( child.mainComponent.remote === false ) {
          return true
        }
      }
    })
  // check if constant is true
  if (isRemote) { 
    console.log('hello')
  }
}

But again it shouldn’t make much of a difference.

1 Like

I tested your code in my file: https://figma.fun/btrmNr, it works perfectly fine and fast. There is zero slowness. So perhaps something is wrong with your file?

1 Like

Thanks, @Gleb, I really appreciate the help and effort that you are putting into it!

I think this issue only shows up when you have complex components with a lot of elements. Based on your file, I added more complex components and was able to replicate the issue: Figma :wink: