Skip to main content
Question

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


Paulus

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)?

21 replies

Gleb
  • Power Member
  • 4708 replies
  • December 31, 2021

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


Paulus
  • Author
  • 11 replies
  • December 31, 2021

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.


Gleb
  • Power Member
  • 4708 replies
  • December 31, 2021

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).


Paulus
  • Author
  • 11 replies
  • December 31, 2021

I will record a video.


ntfromchicago

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…


Paulus
  • Author
  • 11 replies
  • December 31, 2021

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.


Gleb
  • Power Member
  • 4708 replies
  • January 1, 2022

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).


Paulus
  • Author
  • 11 replies
  • January 1, 2022

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.


Paulus
  • Author
  • 11 replies
  • January 1, 2022

@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.


Paulus
  • Author
  • 11 replies
  • January 1, 2022

@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.


Gleb
  • Power Member
  • 4708 replies
  • January 2, 2022

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.


Gleb
  • Power Member
  • 4708 replies
  • January 2, 2022

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?


Paulus
  • Author
  • 11 replies
  • January 2, 2022

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 😉


Pablo_Sanchez

Hello @Paulus Did you find a solution for the selection problem? I have the same situation, trying to find all TextNodes in the user’s selection, when instances involved with a lot of copies, then the performance is degraded.

I would appreciate a workaround in how to approach this issue in order to traverse as quick as possible the user’s selection in searching of TextNodes.

Thank you in advance!


tank666
  • 4871 replies
  • October 9, 2022

Have you tried using findAllWithCriteria?


Pablo_Sanchez

Yes I did a brief test and couldn’t see any great difference. I’ll try again.

One note is that I’m looking for visible text nodes in the current selection, which is more tricky, because I cannot apply the filterAllWithCriteria to the selection object.

Any ideas?


tank666
  • 4871 replies
  • October 9, 2022
const arr = [],
	selection = figma.currentPage.selection;
selection.forEach(node => {
	if (node.type === 'TEXT' && node.visible) {
		arr.push(node)
	}
	else if (node.children) {
		arr.push(...node.findAllWithCriteria({types: ['TEXT']}).filter(v => v.visible))
	}
});
console.log('arr', arr);

Gleb
  • Power Member
  • 4708 replies
  • October 10, 2022

Pablo_Sanchez

Hi @Gleb, yes, we have this in the code, there is an improvement, but when selecting components with a lot of instances, the performance is worse than when you select an isolated instance or frame.

Is there any way to avoid this delay, should the API be slower when selecting components? Why is this behavior happening?

Thanks.


Paulus
  • Author
  • 11 replies
  • October 10, 2022

@Pablo_Sanchez AFAIK there is no fix to this. This is IMHO a Figma bug, I don’t see any logical explanation for why this happens. Not sure also if your case is what I described. A bug that I see is that while using node.children on a Main Component Figma processes also it’s instances (which are not children).


Pablo_Sanchez

Thank you @Paulus at least I know now that there is no room for improvement on our side. Let’s wait for Figma to resolve this internal performance issue.

Thanks to all!


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings