Phoenix Live View Debounce
Tags: elixir, phoenix, liveview
Published: 2019-05-26 12:53:21

---
title: Phoenix Live View Debounce
published: true
description: How to implement debounce in phoenix live view
tags: elixir, phoenix, liveview
cover_image: https://source.unsplash.com/ihFWKicceNk
---

Phoenix Live View is still in beta but a lot of people are building interesting stuff with it. At [Simplificator](https://simplificator.com) we are also trying to use it for both external clients and internal projects.

# Search form

One nice use case of the phoenix live view is to build a dynamic search form that performs some searches while typing.

![](https://thepracticaldev.s3.amazonaws.com/i/5dtcg8l4bb96ht57lo3o.png)

As showed by Chris McChord in his [examples](https://github.com/chrismccord/phoenix_live_view_example/blob/master/lib/demo_web/live/search_live.ex#L6) this is easy to achieve with a live view:

```elixir
def mount(_session, socket) do
  {:ok, assign(socket, query: nil, results: nil, loading: false)}
end

def handle_event("search", %{"query" => query}, socket) do
  send(self(), {:search, query})
  socket = socket
  |> assign(:query, query)
  |> assign(:loading, true)

  {:noreply, socket}
end

def handle_info({:search, query}, socket) do
  socket = socket
  |> assign(:loading, false)
  |> assign(results: search_results(query))
  {:noreply, socket}
end
```

The idea is to react on every key stroke by sending a message to the same process. This message is of the form `{:search, query}` and asynchronously performs the necessary query (for example on the database).

This is a pragmatic approach, but usually we want to avoid to perform too many unnecessary queries while typing. The possibility to set a minimum amount of time between two successive queries is called debounce and it is a very common feature in most javascript framework.

The debounce feature is not available at the moment in phoenix live view but will be implemented in the near future, as you can see from this [discussion](https://elixirforum.com/t/phoenix-liveview-is-now-live/20889/13). Luckily this feature is very easy to implement.

## Debounce

The biggest difference from the previous code snippet is that instead of sending a message to the current process, the message to perform the query is sent after a certain "debounce time" and the query term is stored in the state of the process (the socket in case of the phoenix live view).

This is achieved using `Process.send_after/3` function which returns a timer reference. Then the timer reference is stored in the state (socket) of the live view along with a `loading` flag and the search term.

In case a new query term arrives within the "debounce time", the new query term is stored in the state. Once the "debounce time" is reached, the `:search` message is triggered, and the query performed.

Here you can see the full code snippet:

```elixir
# new query term arrives within the debounce time
def handle_event("search", %{"query" => q}, %{assigns: %{loading: true}} = socket) do
  socket = socket
  |> assign(:query, q)

  {:noreply, socket}
end

def handle_event("search", %{"query" => q}, %{assigns: %{loading: false}} = socket) do
  # debounce time of 300 ms
  timer_ref = Process.send_after(self(), :search, 300)
  socket = socket
  |> assign(:query, q)
  |> assign(:timer_ref, timer_ref)
  |> assign(:loading, true)

  {:noreply, socket}
end

def handle_event("search-final", %{"query" => q}, socket)  do
  Process.cancel_timer(socket.assigns.timer_ref)
  socket = socket
  |> assign(:query, q)

  send(self(), :search)
  {:noreply, socket}
end

def handle_info(:search, socket) do
  socket = socket
  |> assign(:loading, false)
  |> assign(:results, search_results(socket.assigns.query))

  {:noreply, socket}
end

```

As you see the timer reference is used to cancel the future search request when the user will click on the `search` button and "commits" to a search; any future request is canceled (if any) and an immediate search performed.