A simple debounce module that can be used as part of your update functions or directly from views as event msg.
let
(debouncer, cmd) =
Debounce.push DebounceMsg SendRequest model.debounce
in
({model | debouncer = debouncer}, cmd) button
[ onClick (Debounce.event DebounceMsg SendRequest) ]
[ Html.text "Fetch user" ]say you have a controlled text input field that you want to send a api request with its value when the user stops typing.
you dont want to send an api request on every keystroke
import Debounce exposing (Debounce) type alias Model =
{ inputText : String
, debounce : Debounce Msg
}
type Msg
= UpdateInputText String
| SendRequest
| DebounceMsg (Debounce.Msg Msg) init : () -> ( Model, Cmd Msg )
init _ =
( { inputText = ""
, debounce = Debounce.init 500
}
, Cmd.none
) update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UpdateInputText string ->
let
( debounce, cmd ) =
-- here you would want to update the text field on every keystroke
-- SendRequest Cmd gets pushed to the debouncer list of cmds
-- when the user stops typing for 500 (the cooldown period) -> the last cmd gets fired, the rest will be ignored
Debounce.push DebounceMsg SendRequest model.debounce
in
( { model
| debounce = debounce
, inputText = string
}
, cmd
)
SendRequest ->
let
_ =
Debug.log "Sending request" model.inputText
in
-- here you can fire the http request
( model, Cmd.none )
DebounceMsg debounceMsg ->
Debounce.update DebounceMsg debounceMsg model.debounce
|> Tuple.mapFirst (\debounce -> { model | debounce = debounce }) input
[ type_ "text"
, value model.inputText
, onInput UpdateInputText
] []say you want to send a http request on a btn click for any reason,
type alias Model =
{ debounce : Debounce Msg
}
type Msg
= SendRequest
| DebounceMsg (Debounce.Msg Msg)we will not use Debounce.push to add to the list of Debounce cmds, we will push to the cmds list directly from the view with an event in the next step, however, you would still have to create a case for DebounceMsg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SendRequest ->
let
_ =
Debug.log "Sending request" model.inputText
in
-- here you can fire the http request
( model, Cmd.none )
DebounceMsg debounceMsg ->
Debounce.update DebounceMsg debounceMsg model.debounce
|> Tuple.mapFirst (\debounce -> { model | debounce = debounce }) button
[ onClick (Debounce.event DebounceMsg SendRequest) ]
[ Html.text "Click me" ]Debounce.push vs Debounce.eventDebounce.push is useful when you want to update multiple things, only some of those updates you want to debounce
In our first example above, if we were to use Debouce.event rather than Debounce.push like so
input
[ type_ "text"
, value model.inputText
, onInput (UpdateInputText >> Debounce.event DebounceMsg)
] []updating the text field value will then be debounced (which is not the desired behaviour), instead, we can pass UpdateInputText Msg as handler for onInput, and in the update function we can update the text input value and push SendRequest to the Debounce Cmd list
view model =
input
[ type_ "text"
, value model.inputText
, onInput UpdateInputText
] []
update msg model =
case msg of
UpdateInputText string ->
let
( debounce, cmd ) =
Debounce.push DebounceMsg SendRequest model.debounce
in
( { model
| debounce = debounce
, inputText = string
}
, cmd
)