Skip to content

fix: stabilize lifecycle and Elixir 1.20 compatibility - #55

Open
geonwoo-jeong wants to merge 3 commits into
mrdotb:props-diff-and-streamsfrom
geonwoo-jeong:fix/jeo-86-production
Open

fix: stabilize lifecycle and Elixir 1.20 compatibility#55
geonwoo-jeong wants to merge 3 commits into
mrdotb:props-diff-and-streamsfrom
geonwoo-jeong:fix/jeo-86-production

Conversation

@geonwoo-jeong

@geonwoo-jeong geonwoo-jeong commented Aug 14, 2026

Copy link
Copy Markdown

Contributor checklist

Summary

This draft builds on #54 and adds a small set of production hardening changes without altering its props-diff or streams design:

  • make React root cleanup immediate and idempotent when a LiveView hook is destroyed
  • preserve both LiveReact.Encoder.encode/1 and encode/2 while removing deprecated protocol default arguments on Elixir 1.20
  • keep bitstring matching and unused compile-time dependencies warning-free on Elixir 1.20
  • guard Phoenix Ecto form encoding when the optional Ecto/FormData integration is unavailable

Why these changes are needed

React root lifecycle

The previous destroyed() callback deferred unmounting until phx:page-loading-stop. That event is tied to page navigation and is not guaranteed when a LiveView patch only removes the component, so the React root and its effects could remain alive after the hook had been destroyed. The deferred callback also read the mutable this._root later, which could target a different root if the hook state changed in the meantime.

The hook now captures the current root, clears this._root, and unmounts immediately. Clearing the reference before unmounting makes repeated or re-entrant destroyed() calls safe and guarantees that the same root is unmounted exactly once.

Protocol callback compatibility

The encoder previously declared def encode(value, opts \\ []) inside defprotocol. Elixir 1.20 deprecates default arguments in protocol definitions and warns that protocols may only declare callbacks without an implementation. These warnings fail projects that compile dependencies with warnings treated as errors.

The protocol now declares encode/1 and encode/2 explicitly. Each implementation defines encode(value) as a direct delegation to encode(value, []), preserving the existing one-argument API and behavior while making both protocol arities explicit and compatible with Elixir 1.20.

Validation

  • added a lifecycle regression test proving immediate, exactly-once unmounting, including a later phx:page-loading-stop and a repeated destroyed() call
  • verified the encoder retains the same encode(value) to encode(value, []) behavior across its protocol implementations
  • validated the changes on Elixir 1.20.3 with warnings treated as errors and through the Ptah production integration path
  • verified the optional phoenix_ecto boundary without requiring external provider or network calls

Follow-up to #54.

@geonwoo-jeong
geonwoo-jeong force-pushed the fix/jeo-86-production branch from bcbf18f to e1afb48 Compare August 14, 2026 13:17
@geonwoo-jeong
geonwoo-jeong marked this pull request as ready for review August 14, 2026 13:29
@geonwoo-jeong
geonwoo-jeong marked this pull request as draft August 14, 2026 13:39
@geonwoo-jeong
geonwoo-jeong marked this pull request as ready for review August 14, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant