Rbtz. CredoChecks. Warning. PhxHookComponentWithoutStableId
(rbtz_credo_checks v0.4.0)
Copy Markdown
View Source
Basics
This check is disabled by default.
Learn how to enable it via .credo.exs.
This check has a base priority of high and works with any version of Elixir.
Explanation
Requires function components whose template uses phx-hook to bind the
hook target element to a stable DOM id.
Phoenix LiveView hooks are keyed by DOM id, so every hook target must
have a stable, unique id. If the id can be missing or nil at
runtime, multiple instances of the same component collide and LiveView
can't route pushEvents or maintain the hook across patches.
A hook target's id is considered stable when it is any of:
- a literal string on the element (
id="phone-number"), id={@id}bound to an attr declared asattr :id, :string, required: trueorattr :id, :string, default: "<literal>"(binary default), orid={<expr>}where the interpolation references only@<name>assigns that are each themselves declared as a stable-id attr — e.g.id={@id <> "-trigger"}withattr :id, :string, required: true.
The attr name does not have to be literally :id — any name works,
as long as every @<name> referenced inside the id={...}
interpolation is declared with required: true or a binary default:.
Anything else — no id= on the phx-hook element, a bare @<name>
that isn't declared as a stable-id attr, an id={...} expression that
references no assigns or references an unstable assign, or an attr
with default: nil — is flagged.
The check walks each module, tracks attr declarations preceding each
def, and flags any function whose preceding attr block is non-empty
and whose ~H template has a phx-hook-carrying element without a
stable id binding.
Bad
attr :class, :string, default: nil
def phone_number(assigns) do
~H"""
<div id={@id} phx-hook=".PhoneNumber" class={@class}>...</div>
"""
end
# `default: nil` doesn't guarantee a stable id
attr :id, :string, default: nil
def phone_number(assigns) do
~H"""
<div id={@id} phx-hook=".PhoneNumber">...</div>
"""
endGood
attr :id, :string, required: true
attr :class, :string, default: nil
def phone_number(assigns) do
~H"""
<div id={@id} phx-hook=".PhoneNumber" class={@class}>...</div>
"""
end
# attr has a binary default — `@clear_button_id` is always populated
attr :clear_button_id, :string, default: "search-clear-button"
def clear_button(assigns) do
~H"""
<button id={@clear_button_id} phx-hook="InputClearButton">...</button>
"""
end
# derived id is fine when every referenced assign is stable
attr :id, :string, required: true
def trigger(assigns) do
~H"""
<button id={@id <> "-trigger"} phx-hook=".Trigger">...</button>
"""
end
# literal id on the element
def phone_number(assigns) do
~H"""
<div id="phone-number" phx-hook=".PhoneNumber">...</div>
"""
endCheck-Specific Parameters
There are no specific parameters for this check.
General Parameters
Like with all checks, general params can be applied.
Parameters can be configured via the .credo.exs config file.