Live component won't remount inside case statement

Hello, forum!

Hope you’re doing great.

Sorry if this is a duplicated topic, but I couldn’t find another with the same issue.

I have a payment screen where the user selects a payment method, then selects other options and then finishes the purchase. This is controlled by a state called payment_stage and they are rendered by a case statement. Inside the first case match, I render a list with payment methods and above that there’s a live component being rendered with a static normal id prop string.

The issue I’m facing is that when the user selects a method, it changes the payment_stage state to the next stage and the <.live_component id="example-id" other-props={@some_state} /> gets unmounted, so far so good. But the same component is not mounted back when the user goes back one stage, which the user can if he wants to change the payment method. I’ve read that phoenix looks at the id and don’t remount the live_component.

Is there a better way of handling this scenario or even not using the case for these multiple payment stages? Is there a better way of building id’s for live components in these scenarios?

[phoenix 1.7.0]
[phoenix_live_view 0.20]
[erlang 24.3.4]
[elixir 1.13.4]

Hi @dell2duo,

I’ve had something similar recently. I think there is an issue in liveview somewhere but I wasn’t able to extract a minimal reproducible case without spending half a day on it as it seems to be a subtle combination.

This may not be the best approach, but where I resolved it by calculating an id that updates in response to the state change that causes the component to fail to reload. That forces it. It may leave bits of dangling DOM in the browser, but RAM is cheap…

As an example, each time payment_stage changes, you could update the id using the current time or a nano-id.

You could also use the heex :if={...} construct for the component instead of using case and see if that helps.

1 Like

mount and update should run once when it first matches a case. update runs when other-props changes.

<.live_component id={"example-id-#{@payment_stage}"} ... /> should run mount again when @payment_stage changes.

1 Like

This problem as described doesn’t make any sense to me. There might be a bug somewhere else. Can you post the relevant code? It’s much easier for us to read code than a description thereof.

1 Like

Does your case have a catch all condition set to “false” or “nil” by any chance?

For example:

conditional_render = 
  case something do
    "condition-1 -> "foo"
    "condition-2 -> "bar"
    _ -> nil

Setting a component to :if={nil} or :if={false} will stop the live_component being rendered. Not sure how you’re handling the “back” feature or the setting of the case condition in the first place, but it might be worth checking.

1 Like

This is a good solution, but in our case we found out there was 3 divs with the same id and it wasn’t a problem before, but when we started having this option of going back a stage, it uncovered this bug because of the mounting, unmounting and then re-mounting of live components. In the end it was our mistake.

1 Like