Figma.off in if statement doesn't work

i’am trying to link hight of a node with another one and having an option to delete that link and that needs the use of figma.on documentchange but the problem is when deleting the handler by figma.off it dosen’t work i have created an if statement to know the deleting message that have been sent when delete button pressed by adding an id wich is 2

if (pluginMessage.id === 'link-it') {
    const lnode = figma.getNodeById(pluginMessage.nodeid);
    const node = figma.getNodeById(pluginMessage.keysids);
    const id = node?.id as string + lnode?.id as string
    let fn = (event: { documentChanges: any; }) =>{
      for (const change of event.documentChanges) {
        if (change.type === 'PROPERTY_CHANGE') {
          if (change.id === node?.id) {            
            lnode.resize(lnode.width, node.height)
          }
        }
      }
    }
    if (pluginMessage.subid !== 2) {
      //add links into the storage
      figma.clientStorage.setAsync(id,{subid:'linked',node:node?.id as string,link:lnode?.id});
      //do the height changes in figma and post message of the id of nodes to the frontend to put the new links
      lnode.resize(lnode.width, node.height)
      figma.ui.postMessage({node:node?.id, link:lnode?.id, id:'storageadd',subid:'link'})
      //if node height changes change it in the linked node
      figma.on('documentchange', fn)
    }
    else if (pluginMessage.subid === 2) {
      figma.clientStorage.deleteAsync(pluginMessage.delnode)
            
      figma.off('documentchange', fn)
    }
  }

The reason this isn’t working is because fn is defined as a new function every time you are checking the message. The way figma.on and figma.off works is by comparing the values of the supplied functions, which are compared by reference, so creating a new function every time means the function supplied to the on callback and the function supplied to the off callback will never be the same.

do you have any idea to fix this because i have those node and lnode variables that needs to be in the fn function which are created by the message

You are going to want to keep track of the ids outside of your onmessage function, and define your documentchange function outside of the onmessage function as well.

Here is a very rough example of how I would watch for property changes on nodes, then react to those changes. You will need to modify it to work for your needs, but I’ve left some comments so hopefully you can figure out how to move forward:

// create an array of node ids to watch for changes
let watchedNodeIds: string[] = []

// create the documentchange handler
const onDocumentChange = event => {
  for (const change of event.documentChanges) {
    if (change.type === 'PROPERTY_CHANGE') {
      // check the array to see if the changed node id matches an id in the list of ids you are keeping track of
      const watchedId = watchedNodeIds.find(id => id === change.id)

      // if there is a match, get the changed node and do whatever you need to do with it
      if (watchedId) {
        // do stuff with the node
        const node = figma.getNodeById(watchedId)
        node.resize(500, 500)
        node.getPluginData('foo')
        node.remove()
      }
    }
  }
}

// create a variable to keep track of whether the documentchange handler is listening for changes or not
let isWatching = false

// our onmessage handler is now _only_ responsible for adding/removing ids to the array we defined above, as well as attaching/removing our documentchange handler when necessary
figma.ui.onmessage = (message: { type: 'watch' | 'unwatch', nodeId: string }) => {
  if (message.type === 'watch') {
    // add node id to the array if watching for changes
    watchedNodeIds.push(message.nodeId)
  } else {
    // remove node id to the array if no longer watching
    watchedNodeIds = watchedNodeIds.filter(id => id !== message.nodeId)
  }

  // add the event handler if there are ids that need to be watched and we are not actively listening
  if (watchedNodeIds.length > 0 && !isWatching) {
    figma.on('documentchange', onDocumentChange)
    // update isWatching to true since we are now watching for changes
    isWatching = true
    return
  }

  // remove the event handler if there are no ids being watched
  if (watchedNodeIds.length === 0 && isWatching) {
    figma.off('documentchange', onDocumentChange)
    // update isWatching to false since we are no longer watching for changes
    isWatching = false
  }
}

I don’t think I can provide much more technical help outside of this, but good luck!

hello i have done some changes on that but it looks like it is the same as before i let the function outside on message (btw there is other messages that is why i added new types) but it doesn’t work also the is watching variable isn’t working properly sometimes it does sometimes no if you have time take a look at it and let me know if there is a problem

// create an array of node ids to watch for changes
let watchedNodeIds: string[] = []
let watchedlinkedNodeIds: string[] = []
// create the documentchange handler
const onDocumentChange = event => {
  for (const change of event.documentChanges) {
    if (change.type === 'PROPERTY_CHANGE') {
      // check the array to see if the changed node id matches an id in the list of ids you are keeping track of
      const watchedNId = watchedNodeIds.find(id => id === change.id)
      // if there is a match, get the changed node and do whatever you need to do with it
      if (watchedNId) {
        // do stuff with the node
        const lnode = figma.getNodeById(watchedlinkedNodeIds[0])
        const node = figma.getNodeById(watchedNId)
        lnode.resize(lnode.width, node.height)
      }
    }
  }
}
// create a variable to keep track of whether the documentchange handler is listening for changes or not
let isWatching = false

figma.ui.onmessage = (pluginMessage: {selid:string, key:string, height:string|number, id: 'adding'}|{ type: 'watch' | 'unwatch', nodeId: string, lnknodeID: string, id: 'link-it' } | {delnodeId: string ,id: 'delete'}) => {
  //when the add button pressed message see if it is in the stored keys or no
if (pluginMessage.id === 'link-it') {
    console.log(pluginMessage);
    console.log(pluginMessage.nodeId);
    
    // our onmessage handler is now _only_ responsible for adding/removing ids to the array we defined above, as well as attaching/removing our documentchange handler when necessary
    if (pluginMessage.type === 'watch') {
      // add node id to the array if watching for changes
      watchedNodeIds.push(pluginMessage.nodeId)
      watchedlinkedNodeIds.push(pluginMessage.lnknodeID)
    } else {
      // remove node id to the array if no longer watching
      console.log(watchedNodeIds);
      watchedNodeIds = watchedNodeIds.filter(id => id !== pluginMessage.nodeId)
      console.log(watchedNodeIds.length);
      
    }
  
    // add the event handler if there are ids that need to be watched and we are not actively listening
    if (watchedNodeIds.length > 0 && isWatching) {
      console.log('handler on');
      const lnode = figma.getNodeById(watchedlinkedNodeIds[0])
      const node = figma.getNodeById(watchedNodeIds[0])
      const id = node?.id as string + lnode?.id as string

      figma.ui.postMessage({node:node?.id, link:lnode?.id, id:'storageadd',subid:'link'})
      figma.clientStorage.setAsync(id,{subid:'linked',node:node?.id as string,link:lnode?.id});
      //do the height changes in figma and post message of the id of nodes to the frontend to put the new links
      lnode.resize(lnode.width, node.height)
      figma.on('documentchange', onDocumentChange)
      // update isWatching to true since we are now watching for changes
      isWatching = true
    }
  
    // remove the event handler if there are no ids being watched
    if (watchedNodeIds.length === 0 && !isWatching) {
      console.log('handler off');
      figma.clientStorage.deleteAsync(pluginMessage.nodeId)
      figma.off('documentchange', onDocumentChange)
      console.log('shit');
      
      // update isWatching to false since we are no longer watching for changes
      isWatching = false
    }
  }
}