Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add debouncing example #256

Merged
merged 4 commits into from
Feb 17, 2024

Conversation

DLakomy
Copy link
Contributor

@DLakomy DLakomy commented Feb 17, 2024

Recently I've been thinking how to add debouncing to an input using Tyrian. I've managed to find a good example in Elm. Perhaps something more declarative would be better, but I think this simple example is a good start. If there are any suggestions, I'll be happy to hear them.

davesmith00000
davesmith00000 previously approved these changes Feb 17, 2024
Copy link
Member

@davesmith00000 davesmith00000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, I love it! 🎉

I have approved because it's good enough as it is, and I'm delighted to have the contribution. I have however, also left some comments for your consideration. Mostly style points to encourage people to use best practices. The only comment that might be a technical issue, is that I think you need to reset the debouncer once you release the value.

examples/debouncing/src/main/scala/example/Main.scala Outdated Show resolved Hide resolved
examples/debouncing/src/main/scala/example/Main.scala Outdated Show resolved Hide resolved
case _ =>
Sub.None

final case class Model(value: String, debouncer: Option[(String, Int)])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(String, Int)

Only if you want to: It would be interesting to model the debouncer more intentionally and see what it does to your code, something like (I'm making this up, untested, probably incomplete etc):

final case class Debouncer(value: String, timeout: Int, timeRemaining: Int):
  def reset: Debouncer = Debouncer("", timeout, timeout)
  def update(timePassed): Debouncer = Debouncer(value, timeout, timeRemaining - timePassed)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final class Debouncer[A](val value: A, timeRemaining: Int):
  def update(timePassed: Int): Debouncer[A] =
    Debouncer(value, timeRemaining - timePassed)
  def isSteady = timeRemaining <= 0

I've implemented something like this (+ necessary changes in other parts of code; the model now has debouncer: Option[Debouncer[String]]). I haven't pushed this changes, because to be honest I'm not sure if it's better than the original example (in terms of being easy to follow). Some logic is better encapsulated, but subscriptions and update definitions still need to be aware of the Debouncer and actively manage it's state.

Perhaps something like onInput(Msg.UpdateValue(_)).debounced(100.millis), with no additional logic in subscriptions and update, would be ideal (if I remember correctly, in Laminar the API is similar to this). Before writing the example I tried to come up with something like this (possibly involving some changes to Tyrian itself), but for now I haven't had an idea nor enough time :( Hence I decided to at least provide an example, as a proof of concept.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but subscriptions and update definitions still need to be aware of the Debouncer and actively manage it's state.

Yes, that's expected, it's part of how the Elm architecture scales that, if you want to use this pattern, you just need to accept it and go along with it. However you construct a "component" like this, you always have to manually tie it into the various systems that will update it, somehow. But as I say, that's ok, it's just part of the pattern and you get used to it.

But I also think not including it is completely fine. If this version is easier to follow, that's totally ok with me. Thanks for giving it a shot! 👍

examples/debouncing/src/main/scala/example/Main.scala Outdated Show resolved Hide resolved
Copy link
Member

@davesmith00000 davesmith00000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@davesmith00000 davesmith00000 merged commit 3a231cb into PurpleKingdomGames:main Feb 17, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants