5 ms·
I'm curious what is specific to Phoenix that made this so productive for that project? Is the frontend using something like HTMX?
by juliend2 1y ago
I'm curious what is specific to Phoenix that made this so productive for that project? Is the frontend using something like HTMX?
- shawa_a_a 1y agoThey're probably using some features of LiveView; I'm not too familiar with how HTMX works, but with LiveView you can define all of your logic and state handling on the _backend_, with page diffs pushed to the client over a websocket channel (all handled out of the box). It comes with some tradeoffs compared to fully client-side state, but it's a really comfortable paradigm to program in, especially if you're not from a frontend background, and really clicks with the wider Elixir/Erlang problem solving approach. https://hexdocs.pm/phoenix_live_view/js-interop.html#handling-server-pushed-events https://hexdocs.pm/phoenix_live_view/js-interop.html#handlin... Hooks let you do things like have your DOM update live, but then layer on some JS in response. For example you could define a custom `<chart>` component, which is inserted into the DOM with `data-points=[...]`, and have a hook then 'hydrate' it with e.g. a D3 or VegaLite plot. Since Phoenix/LiveView is handling the state, your JS needs only be concerned about that last-mile JS integration; no need to pair it with another virtual DOM / state management system. https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook https://hexdocs.pm/phoenix_live_view/js-interop.html#client-...
- brendanmc6 1y agoThe 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/ https://bevacqua.github.io/dragula/ [2] https://supabase.com/docs/guides/realtime/architecture https://supabase.com/docs/guides/realtime/architecture