mikesol / purescript-deku

A PureScript web UI framework

Home Page:https://purescript-deku.surge.sh/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Nested dyns result in JS error

mikesol opened this issue · comments

Here's a minimal repro of a nested dyn that results in a JS error. Click Add and then Cancel to to produce the error.

module Main where

import Prelude


import Data.Foldable (oneOf, traverse_)
import Data.Maybe (Maybe(..), isJust)
import Data.Tuple.Nested ((/\))
import Deku.Attribute (cb, (:=))
import Deku.Control (text_, (<#~>))
import Deku.Core (Domable, dyn)
import Deku.DOM as D
import Deku.Do as Deku
import Deku.Hooks (useDyn_, useHot, useState')
import Deku.Lifecycle (onWillMount)
import Deku.Listeners (click_)
import Deku.Toplevel (runInBody)
import Effect (Effect)
import Effect.Aff (Milliseconds(..), delay, launchAff_)
import Effect.Class (liftEffect)

import FRP.Event (Event, burning, create, subscribe)

type Rec = { code :: Int, desc :: String }
type Recs = Array Rec

main :: Effect Unit
main = do
  { event } <- create
  let
    init =
      [ { code: 1, desc: "Desc for 1" }
      ]
  { event: recs } <- burning init event
  runInBody (testFunc recs)

data ItemLifecycle = Old Rec | New Rec

testFunc :: forall lock payload. Event Recs -> Domable lock payload
testFunc recs = Deku.do
  setUpdating /\ updating <- useHot Nothing
  setItem /\ item <- useState'

  onWillMount
    ( launchAff_ do
        delay $ Milliseconds 0.0
        liftEffect do
          uns <- subscribe recs $ traverse_ $ Old >>> setItem
          uns
    ) $ Deku.do
    let
      disabled = (_ <#> if _ then D.Disabled := "true" else D.Disabled := unit)

    D.div_
      [ D.button
          ( oneOf
              [ disabled $ updating <#> isJust
              , click_ $ cb \_ -> do
                  setItem $ New { code: 0, desc: "" }
                  setUpdating $ Just 0
              ]
          )
          [ text_ "Add" ]

      , D.div_
          [ dyn $ item <#> \it' ->
              Deku.do
                { remove } <- useDyn_

                let
                  it = case it' of
                    Old i -> i
                    New i -> i
                  new = case it' of
                    New _ -> true
                    _ -> false

                updating <#~> \mbUpdCode ->
                  Deku.do
                    let
                      thisUpdating = (Just it.code == mbUpdCode)

                    D.div_ $
                      if thisUpdating then
                        [ D.button
                            ( click_ $ cb \_ -> do
                                if new then remove else pure unit
                                setUpdating Nothing
                            )
                            [ text_ "Cancel" ]
                        ]
                      else if it.code /= -1 then
                        [ text_ $ show it.code
                        ]
                      else []
          ]
      ]

Here's a log of the steps described above and all of the action in Interpret.js prior to the crash.

index.js:551 [webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.
log.js:24 [HMR] Waiting for update signal from WDS...
foreign.js:744 makeRoot {id: 'deku-root', root: body}id: "deku-root"root: body[[Prototype]]: Object
foreign.js:252 makeDynBeacon {id: '-648823', parent: Just, scope: Local, dynFamily: Nothing, pos: Nothing}dynFamily: Nothing {}id: "-648823"parent: Just {value0: 'deku-root'}pos: Nothing {}scope: Local {value0: 'rootScope'}[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-648823', parent: 'deku-root', pos: Nothing, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-648823"parent: "deku-root"pos: Nothing {}[[Prototype]]: Object
foreign.js:398 makeElement {id: '-903457', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '-648823'}id: "-903457"parent: Just {value0: 'deku-root'}pos: Nothing {}scope: Local {value0: 'rootScope'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-903457', parent: 'deku-root', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '-648823'}ez: falseid: "-903457"parent: "deku-root"pos: Nothing {}[[Prototype]]: Object
foreign.js:398 makeElement {id: '-806914', parent: Just, scope: Local, tag: 'button', pos: Just, …}dynFamily: Nothing {}id: "-806914"parent: Just {value0: '-903457'}pos: Just {value0: 0}scope: Local {value0: 'rootScope'}tag: "button"[[Prototype]]: Object
foreign.js:606 unsetAttribute {id: '-806914', key: 'disabled'}id: "-806914"key: "disabled"[[Prototype]]: Object
foreign.js:566 setCb {id: '-806914', key: 'click', value: ƒ}id: "-806914"key: "click"value: ƒ (x)[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-806914', parent: '-903457', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-806914"parent: "-903457"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:445 makeText {id: '-710371', parent: Just, pos: Just, scope: Local, dynFamily: Nothing}dynFamily: Nothing {}id: "-710371"parent: Just {value0: '-806914'}pos: Just {value0: 0}scope: Local {value0: 'rootScope'}[[Prototype]]: Object
foreign.js:632 setText {id: '-710371', text: 'Add'}id: "-710371"text: "Add"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-710371', parent: '-806914', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-710371"parent: "-806914"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:398 makeElement {id: '-613828', parent: Just, scope: Local, tag: 'div', pos: Just, …}dynFamily: Nothing {}id: "-613828"parent: Just {value0: '-903457'}pos: Just {value0: 1}scope: Local {value0: 'rootScope'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-613828', parent: '-903457', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-613828"parent: "-903457"pos: Just {value0: 1}[[Prototype]]: Object
foreign.js:252 makeDynBeacon {id: '-517285', parent: Just, scope: Local, dynFamily: Nothing, pos: Just}dynFamily: Nothing {}id: "-517285"parent: Just {value0: '-613828'}pos: Just {value0: 0}scope: Local {value0: 'rootScope'}[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-517285', parent: '-613828', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-517285"parent: "-613828"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:252 makeDynBeacon {id: '-131113', parent: Just, scope: Local, dynFamily: Just, pos: Just}dynFamily: Just {value0: '-517285'}id: "-131113"parent: Just {value0: '-613828'}pos: Just {value0: 0}scope: Local {value0: '-227656'}[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-131113', parent: '-613828', pos: Just, dynFamily: Just, ez: false}dynFamily: Just {value0: '-517285'}ez: falseid: "-131113"parent: "-613828"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:398 makeElement {id: '255059', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '-131113'}id: "255059"parent: Just {value0: '-613828'}pos: Nothing {}scope: Local {value0: '158516'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '255059', parent: '-613828', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '-131113'}ez: falseid: "255059"parent: "-613828"pos: Nothing {}[[Prototype]]: Object
foreign.js:445 makeText {id: '351602', parent: Just, pos: Just, scope: Local, dynFamily: Nothing}dynFamily: Nothing {}id: "351602"parent: Just {value0: '255059'}pos: Just {value0: 0}scope: Local {value0: '158516'}[[Prototype]]: Object
foreign.js:632 setText {id: '351602', text: '1'}id: "351602"text: "1"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '351602', parent: '255059', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "351602"parent: "255059"pos: Just {value0: 0}[[Prototype]]: Object
****************************
foreign.js:252 makeDynBeacon {id: '737774', parent: Just, scope: Local, dynFamily: Just, pos: Just}dynFamily: Just {value0: '-517285'}id: "737774"parent: Just {value0: '-613828'}pos: Just {value0: 0}scope: Local {value0: '641231'}[[Prototype]]: Object
foreign.js:33 attributeParent {id: '737774', parent: '-613828', pos: Just, dynFamily: Just, ez: false}dynFamily: Just {value0: '-517285'}ez: falseid: "737774"parent: "-613828"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:398 makeElement {id: '-876055', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '737774'}id: "-876055"parent: Just {value0: '-613828'}pos: Nothing {}scope: Local {value0: '-972598'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-876055', parent: '-613828', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '737774'}ez: falseid: "-876055"parent: "-613828"pos: Nothing {}[[Prototype]]: Object
foreign.js:445 makeText {id: '-779512', parent: Just, pos: Just, scope: Local, dynFamily: Nothing}dynFamily: Nothing {}id: "-779512"parent: Just {value0: '-876055'}pos: Just {value0: 0}scope: Local {value0: '-972598'}[[Prototype]]: Object
foreign.js:632 setText {id: '-779512', text: '0'}id: "-779512"text: "0"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-779512', parent: '-876055', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-779512"parent: "-876055"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:522 setProp {id: '-806914', key: 'disabled', value: 'true'}id: "-806914"key: "disabled"value: "true"[[Prototype]]: Object
foreign.js:398 makeElement {id: '-393340', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '-131113'}id: "-393340"parent: Just {value0: '-613828'}pos: Nothing {}scope: Local {value0: '-489883'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-393340', parent: '-613828', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '-131113'}ez: falseid: "-393340"parent: "-613828"pos: Nothing {}[[Prototype]]: Object
foreign.js:445 makeText {id: '-296797', parent: Just, pos: Just, scope: Local, dynFamily: Nothing}dynFamily: Nothing {}id: "-296797"parent: Just {value0: '-393340'}pos: Just {value0: 0}scope: Local {value0: '-489883'}[[Prototype]]: Object
foreign.js:632 setText {id: '-296797', text: '1'}id: "-296797"text: "1"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '-296797', parent: '-393340', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "-296797"parent: "-393340"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:866 disconnectElement {id: '255059', scope: Local, parent: '-613828', scopeEq: ƒ}id: "255059"parent: "-613828"scope: Local {value0: '158516'}scopeEq: ƒ (x)[[Prototype]]: Object
foreign.js:398 makeElement {id: '89375', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '737774'}id: "89375"parent: Just {value0: '-613828'}pos: Nothing {}scope: Local {value0: '-7168'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '89375', parent: '-613828', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '737774'}ez: falseid: "89375"parent: "-613828"pos: Nothing {}[[Prototype]]: Object
foreign.js:398 makeElement {id: '185918', parent: Just, scope: Local, tag: 'button', pos: Just, …}dynFamily: Nothing {}id: "185918"parent: Just {value0: '89375'}pos: Just {value0: 0}scope: Local {value0: '-7168'}tag: "button"[[Prototype]]: Object
foreign.js:566 setCb {id: '185918', key: 'click', value: ƒ}id: "185918"key: "click"value: ƒ (x)[[Prototype]]: Object
foreign.js:33 attributeParent {id: '185918', parent: '89375', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "185918"parent: "89375"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:445 makeText {id: '282461', parent: Just, pos: Just, scope: Local, dynFamily: Nothing}dynFamily: Nothing {}id: "282461"parent: Just {value0: '185918'}pos: Just {value0: 0}scope: Local {value0: '-7168'}[[Prototype]]: Object
foreign.js:632 setText {id: '282461', text: 'Cancel'}id: "282461"text: "Cancel"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '282461', parent: '185918', pos: Just, dynFamily: Nothing, ez: true}dynFamily: Nothing {}ez: trueid: "282461"parent: "185918"pos: Just {value0: 0}[[Prototype]]: Object
foreign.js:866 disconnectElement {id: '-876055', scope: Local, parent: '-613828', scopeEq: ƒ}id: "-876055"parent: "-613828"scope: Local {value0: '-972598'}scopeEq: ƒ (x)[[Prototype]]: Object
****************************
foreign.js:866 disconnectElement {id: '737774', scope: Local, parent: '-613828', scopeEq: ƒ}id: "737774"parent: "-613828"scope: Local {value0: '641231'}scopeEq: ƒ (x)[[Prototype]]: Object
foreign.js:606 unsetAttribute {id: '-806914', key: 'disabled'}id: "-806914"key: "disabled"[[Prototype]]: Object
foreign.js:398 makeElement {id: '668633', parent: Just, scope: Local, tag: 'div', pos: Nothing, …}dynFamily: Just {value0: '-131113'}id: "668633"parent: Just {value0: '-613828'}pos: Nothing {}scope: Local {value0: '572090'}tag: "div"[[Prototype]]: Object
foreign.js:33 attributeParent {id: '668633', parent: '-613828', pos: Nothing, dynFamily: Just, ez: false}dynFamily: Just {value0: '-131113'}ez: falseid: "668633"parent: "-613828"pos: Nothing {}[[Prototype]]: Object
foreign.js:206 
        
       Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at eval (webpack://try-idea/./output/Deku.Interpret/foreign.js?:206:19)
    at eval (webpack://try-idea/./output/Deku.Interpret/foreign.js?:212:13)
    at eval (webpack://try-idea/./output/Effect.Uncurried/foreign.js?:26:17)
    at eval (webpack://try-idea/./output/FRP.Event/index.js?:865:13)
    at eval (webpack://try-idea/./output/FRP.Event/index.js?:922:28)
    at eval (webpack://try-idea/./output/Effect/foreign.js?:19:16)
    at eval (webpack://try-idea/./output/Effect/foreign.js?:19:20)
    at eval (webpack://try-idea/./output/Effect/foreign.js?:19:16)
    at eval (webpack://try-idea/./output/FRP.Event/index.js?:925:19)
    at eval (webpack://try-idea/./output/FRP.Event/index.js?:924:28)

This has been fixed in the most recent Deku, closing but will reopen a new issue should the problem persist.