dwyl / phoenix-liveview-counter-tutorial

🤯 beginners tutorial building a real time counter in Phoenix 1.7.14 + LiveView 1.0 ⚡️ Learn the fundamentals from first principals so you can make something amazing! 🚀
https://livecount.fly.dev/
GNU General Public License v2.0
374 stars 36 forks source link

Feedback on tutorial (from a beginner's perspective) #227

Open j-n-f opened 2 hours ago

j-n-f commented 2 hours ago

Total elixir newbie here (so my ignorance is pretty high, but maybe this is a useful perspective).

Invoking CounterWeb.Endpoint.broadcast_from sends a message from the current process self() on the @topic, the key is "inc" and the value is the new_state.assigns Map.

I think for people coming from other languages/frameworks it's a bit confusing to mention that the key is "inc". I don't see that key pop up anywhere else after that, or see how the "inc" value is relevant. It looks like the new state of :val is already set and sent to the topic. Is it going to call handle_event("inc", ...) again? It doesn't look like it, it seems like handle_info(...) is going to update assigns for all the connected clients.

To put the question another way, why should I pass "inc" instead of "foo"?

Everything else is clear, but that's the one bit that doesn't make sense.

j-n-f commented 2 hours ago

Just piggybacking here:

This is fine when the template is small like in this counter, but in a bigger App like our MPV it's a good idea to split the template into a separate file to make it easier to read and maintain.

I think MPV is a typo.

j-n-f commented 2 hours ago

Another oddity:

  def render(assigns) do
    ~H"""
    <div class="text-center">
      <h1 class="text-4xl font-bold text-center"> Counter: <%= @val %> </h1>
      <.button phx-click="dec" class="w-20 bg-red-500 hover:bg-red-600">-</.button>
      <.button phx-click="inc" class="w-20 bg-green-500 hover:bg-green-600">+</.button>
    </div>
    """
  end

Initial example shows <.button ...>

but later (when the template goes in its own file):

  def render(assigns) do
    ~H"""
    <div class="text-center">
      <h1 class="text-4xl font-bold text-center"> Counter: <%= @val %> </h1>
      <button phx-click="dec" class={btn("red")}>
        -
      </button>
      <button phx-click="inc" class={btn("green")}>
        +
      </button>
    </div>
    """
  end

The . prefix disappears. No explanation is given.

I think most people will find the . prefix most surprising, so maybe a little explanation can be given for what that means after showing it the first time, or at least mention that it will be explained later.

j-n-f commented 1 hour ago

Finally, we need make some changes to the LiveView itself, it now has less to do!

defmodule CounterWeb.Counter do
  use CounterWeb, :live_view
  alias Counter.Count
  alias Phoenix.PubSub

  @topic Count.topic

  def mount(_params, _session, socket) do
    if connected?(socket) do
      #                                why are we using Counter.PubSub?
      #                vvvvvvvvvvvvvv  this is aliased the same as Phoenix.PubSub
      PubSub.subscribe(Counter.PubSub, @topic)
    end
    {:ok, assign(socket, val: Count.current()) }
  end

  # ...
end

This bit also doesn't make sense. I know that Counter.PubSub could be something completely different, but it really does look like it references the same thing.

j-n-f commented 1 hour ago

Create a file with the path: lib/counter_web/live/counter_state.ex and add the following:

  def handle_call(:current, _from, count) do
     {:reply, count, count}
  end
  defp make_change(count, change) do
    new_count = count + change
    PubSub.broadcast(Counter.PubSub, topic(), {:count, new_count})
    {:reply, new_count, new_count}
  end

Why is the count value returned twice in these functions?

What is Counter.PubSub? The module is called Counter.Count. I don't see where Counter.PubSub is defined. I'm guessing alias Phoenix.PubSub would be fully qualified in this scope as Counter.Count.PubSub so it wouldn't be referring to that. It's not anywhere in counter.ex.


edit: found it: https://github.com/dwyl/phoenix-liveview-counter-tutorial/blob/main/lib/counter/application.ex#L16

I don't think the application.ex was covered. application.ex is mentioned in passing, but it seems like an important element of how the framework functions. It might be nice to have it explained, what it does, how it relates to other things, where/if it has to be registered anywhere else in the application, etc. At a minimum, maybe something like "you can read about what this file is and what it does at ".

j-n-f commented 1 hour ago

Some of these could be considered more general bits of Elixir trivia (namespacing, etc.).

I think with the earlier claim:

Basic familiarity with Elixir syntax is recommended but not essential

It's looking to me like it probably is essential. Maybe there are ways to gently fold (some of) these bits of knowledge into the tutorial.

j-n-f commented 1 hour ago

And that's all I've got for now.

It's a massive effort to mix together this amount of prose and code and I appreciate all the work you've put into it.

One thing that impressed me lately is the format of the Svelte tutorial. I don't know if there's an interactive tool like this for Elixir, but I could see the content in this tutorial fitting that kind of format really nicely. I did a little search, but so far I don't see anything that would run the BEAM in wasm or something like that, but it would be really cool if it did.