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

Any chance of an example showing how to debounce rendering an input

jerbaroo opened this issue · comments

Love the library, chose it for a recent project I am working on. Struggling to figure some things out, but getting there.

I am a bit stumped on how to debounce the rendering of error text for this input component. The code should be fairly illustrative on what I am trying to accomplish:

input _ initialMay onKeyup = Deku.do
  -- We save the raw input to calculate error messages.
  setValueMay /\ valueMay <- useState initialMay
  D.div
    [ ]
    [ D.input
        ( [ DA.value $ valueMay <#> maybe "" repr
          , DL.keyup_ $ \event -> Event.withTargetValue (toEvent event) \s -> do
              setValueMay $ Just s
              onKeyup s -- Should be executed without any debounce delay.
          ]
            <> flip (maybe []) initialMay \v -> [ DA.value_ v ]
        )
        []
    , D.div
        [ DA.klass_ "input-error" ]
        [ valueMay <#~> case _ of 
            -- Should only be rendered after a debounce period.
            Nothing -> text_ ""
            Just value -> case parse value of
              Right (_ :: a) -> text_ ""
              Left error ->
                -- Only show the error if input is non-empty.
                text_ $ if String.length value == 0 then "" else error
        ]
    ]

Yes, I can put this together & thanks for requesting it!

Thanks for your patience: deku went through a big rewrite for this sort of thing recently & I can now report how to go about this.

You'll want to create a custom with combinator, ie withDebounce. You can build it off of withDelay (https://github.com/mikesol/purescript-hyrule/blob/main/src/FRP/Event/Time.purs) and then do ie under Op (withDebounce nMilliseconds) setValueMay $ Just s. The hyrule tests use this pattern quite a bit: https://github.com/mikesol/purescript-hyrule/blob/main/test/Main.purs, check withTime in there for example.

It's a fun little exercise to create a withDebounce from withDelay. GIve it a try! If it's too challenging, let me know and I can help out. Here are a couple hints.

  1. The signature will be withDebounce :: forall a. Int -> Op (Effect Unit) a -> Op (Effect Unit) a.
  2. It will use withDelay, so some intermediary function will need to have the signature: Op (Effect Unit) a -> Op (Effect Unit) (Either TimeoutId (Tuple TimeoutId a)). This feels a little backwards when you look at it because it looks like it's producing the type of withDelay but it's actually consuming it. This is one of the neat things about Op.