Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The big win for me has been the built-in PubSub primitives plus LiveView. Since the backend is already maintaining a WebSocket connection with every client, it's trivial to push updates.

Here is an example. Imagine something like a multiplayer Google Forms editor that renders a list of drag-droppable cards. Below is a complete LiveView module that renders the cards, and subscribes to "card was deleted" and "cards were reordered" events.

```

  defmodule MyApp.ProjectLive.Edit do
    use MyApp, :live_view
    import MyApp.Components.Editor.Card

    def mount(%{"project_id" => id}, _session, socket) do
      # Subscribe view to project events
      Phoenix.PubSub.subscribe(MyApp.PubSub, "project:#{id}")
      project = MyApp.Projects.get_project(id)

      socket =
        socket
        |> assign(:project, project)
        |> assign(:cards_drag_handle_class, "CARD_DRAG_HANDLE")

      {:ok, socket}
    end

    def handle_info({:cards, :deleted, card_id}, socket) do
      # handle project events matching signature: `{:cards, :deleted, payload}`
      cards = Enum.reject(socket.assigns.project.cards, fn card -> card.id == card_id end)
      project = %{socket.assigns.project | cards: cards}
      socket = assign(socket, :project, project)
      # LiveView will diff and re-render automatically
      {:noreply, socket}
    end

    def handle_info({:cards, :reordered, card_change_list}, socket) do
      # omitted for brevity, same concept as above
      {:noreply, socket}
    end

    def render(assigns) do
      ~H"""
      <div>
        <h1>{@project.name}</h1>
        <div
          id="cards-drag-manager"
          phx-hook="DragDropMulti"
          data-handle-class-name={@cards_drag_handle_class}
          data-drop-event-name="reorder_cards"
          data-container-ids="cards-container"
        />
        <div class="space-y-4" id="cards-container">
          <.card
            :for={card <- @project.cards}
            card={card}
            cards_drag_handle_class={@cards_drag_handle_class}
          />
        </div>
      </div>
      """
    end
  end
```

What would this take in a React SPA? Well of course there are tons of great tools out there, like Cloud Firestore, Supabase Realtime, etc. But my app is just a vanilla postgres + phoenix monolith! And it's so much easier to test. Again, just using the built-in testing libraries.

For rich drag-drop (with drop shadows, auto-scroll, etc.) I inlined DragulaJS[1] which is ~1000 lines of vanilla .js. As a React dev I might have been tempted to `npm install` something like `react-beautiful-dnd`, which is 6-10x larger, (and is, I just learned, now deprecated by the maintainers!!)

The important question is, what have I sacrificed? The primary tradeoff is that the 'read your own writes' experience can feel sluggish if you are used to optimistic UI via React setState(). This is a hard one to stomach as a react dev. But Phoenix comes with GitHub-style viewport loading bars which is enough user enough feedback to be passable.

p.s. guess what Supabase Realtime is using under the hood[2] ;-)

[1] https://bevacqua.github.io/dragula/ [2] https://supabase.com/docs/guides/realtime/architecture



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: