Search code examples
lifecyclepurescripthalogen

What's the best way to "destroy" a purescript-halogen component when you're done using it?


I'm using Halogen to control a menu system for my application, and I'm wondering how I can "destroy" a Halogen component. Right now, I have a function that creates a submenu component on a div with a particular ID. Throughout the lifecycle of the app, the submenu could be visited multiple times, so currently multiple instances of the component are created whenever the visitSubMenu function is called.

import Halogen.Aff as HA
import Halogen.VDom.Driver (runUI)

visitSubMenu = do
  elem <- HA.selectElement "#submenu"
  case elem of
    (Just element) -> do
      liftEff $ HA.runHalogenAff do
        io <- runUI subMenuComponent unit element
        -- other code using component io...

Ideally, I'd like the submenu component to be removed when the user is done with their work in the submenu (i.e. the HTML injected into #submenu is removed, other cleanup happens). Is that sort of thing possible? I realize I could setup the component only one time at the start of the app, and keep it alive throughout the life of the app, but it seems better to me if I only set it up when it's needed.


I doubt this is the right way to do it, but my initial thought is I could create a lifecycleComponent, and when I want to destroy it, I can send a Finalize query and then manually remove the parent element from the DOM.


Solution

  • In the end, the only solution I found was to send a "Cleanup" query to the component, and then remove the root DOM element of the halogen component.

    FFI

    exports.removeElementById = function(id) {
      return function() {
        var e = document.getElementById(id);
        if (e) e.remove();  
      }
    }
    

    Halogen Component Module

    foreign import removeElementById :: String -> Eff (dom :: DOM | e) Unit
    
    removeSelf :: forall e. Aff (dom :: DOM | e) Unit
    removeSelf = liftEff $ removeElementById componentId
    
    componentId :: String
    componentId = "unique-id-for-component"
    
    render :: State -> H.ComponentHTML Query
    render state = HH.div [HP.id_ componentId] []
    

    So then I can call removeSelf when I'm finished using the component.