live_props v0.2.2 LiveProps View Source

LiveProps is a library for managing properties and state within Phoenix LiveViews and Phoenix LiveComponents.

Features

  • Declaratively define props and state, initialize default values, and compute derived values using the LiveProps.Prop.prop/3 and LiveProps.Prop.state/3 macros.

  • Supports required props

  • Supports automatic re-computation of computed props and state

  • Props automatically added to module documentation.

Example

Inside a LiveView or LiveComponent, you must use LiveProps.LiveView or LiveProps.LiveComponent, respectively. LiveComponents can have state and props, while a LiveView can only have state, so we'll look at an example LiveComponent to demonstrate both.

Here's a simple Button component that just has props:

defmodule ButtonComponent do
  use Phoenix.LiveComponent
  use LiveProps.LiveComponent

  prop :class, :string, default: "button"
  prop :text, :string, default: "Click me"
  prop :on_click, :string, default: "click_button"

  def render(assigns) do
    ~L"""
    <button class="<%= @class %>"
            phx-click="<%= @on_click %>">
      <%= @text %>
    </button>
    """
  end

In this example we define three props that will be automatically assigned default values, so you don't have to define your own mount or update callbacks to do it yourself.

defmodule MyAppWeb.ThermostatComponent do
  # If you generated an app with mix phx.new --live,
  # the line below would be: use MyAppWeb, :live_component
  use Phoenix.LiveComponent
  use LiveProps.LiveComponent

  prop :user_id, :integer, required: true
  prop :temperature, :float, compute: :get_temperature

  state :mode, :atom, default: :verbose

  def render(assigns) do
    ~L"""
    <%= case @mode  do %>
      <% :verbose -> %>
        Current temperature: <%= @temperature %>

      <% _ -> %>
        <%= @temperature %>
    <% end %>
    <button phx-click="toggle-mode" phx-target="<%= @myself %>">Toggle mode</button>
    """
  end

  def get_temperature(assigns) do
    Thermostat.get_user_reading(assigns.user_id)
  end

  def handle_event("toggle-mode", _, %{assigns: assigns} = socket) do
    new_mode = if assigns.mode == :verbose, do: :compact, else: :verbose

    {:noreply, set_state(socket, :mode, new_mode)}
  end
end

Our component requires a :user_id prop, which it uses to fetch the temperature. Since it is required, an error will be raised if you forget to pass it in.

We also have the :temperature prop, which is a computed prop. This will be re-calculated automatically anytime the :user_id prop changes. It is calculated by get_temperature/1 which takes the socket assigns as an argument and returns the value to be assigned. Calculations are run in the order defined so we could add even more computed props which depend on the temperature assign.

Lastly, the component has a state called :mode which controls the display. We've given it a default value, which is assigned on mount behind the scenes. We could also add computed states that depend on other states. In the "toggle_mode" handler we use LiveProps.States.set_state/3 to update the :mode. We could have just done a regular Phoenix.LiveView.assign/3 call but using set_state/3 is useful when we want to trigger the re-calculation of other states as well (in this case, there are none).

Notice what our component does not have: a Phoenix.LiveComponent.mount/1, Phoenix.LiveComponent.update/2 or Phoenix.LiveComponent.preload/1 callback. LiveProps handles that for you, by injecting lightweight callbacks behind the scenes.

You can still define your own callbacks if you need to, and everything should continue to work. In a LiveComponent, any callbacks you define will be run after the the LiveProps callbacks (i.e. defaults and computed values will already be assigned to the socket). The LiveProps.LiveComponent documentation has additional information on component lifecycles.

This module is not intended to be used directly but rather by means of LiveProps.LiveView and LiveProps.LiveComponent. Please see docs for those modules for additional information.