-
Notifications
You must be signed in to change notification settings - Fork 893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: component unmount callback #1238
Comments
Can you please explain the use case in particular? Sure, React has them, but why would they be necessary in LV which server side? Which kind of functionality could you only implement with it? Thank you. |
Yep for sure: The toy example I was playing with was cleaning up after a More realistically, I'm trying to let components have more ability to subscribe to things directly:
You are right to push back on it being as fundamental as it is in the browser environment - I went to usehooks.com to see how many of those effect hooks made sense in the context of live view, and largely they do not, since they subscribe to native browser APIs that aren't available from a server environment. The interface that I am currently targeting for a "component that is implemented with react-style hooks" looks like this: defmodule DemoWeb.Counter do
import DemoWeb.HookComponent.Hooks # gives me use_state and use_effect
import Phoenix.LiveView.Helpers
def describe(assigns, hooks) do
{count, set_count, hooks} = use_state(0, hooks)
hooks =
use_effect(
fn ->
{:ok, ref} =
:timer.apply_interval(1000, DemoWeb.Counter, :_block_timer_apply, [
fn ->
set_count.(fn count -> count + 1 end)
end
])
# the effect returns a function that says how to "cancel/undo" the effect
fn ->
:timer.cancel(ref)
end
end,
[],
hooks
)
{
~L"""
<div style="color: red"><%= count %></div>
""",
hooks
}
end
def _block_timer_apply(func) do
func.()
end
end I have it largely working with a small patch to the outermost liveview, but an abandoned Counter component would cause more timer functions to apply unnecessarily. A worse example in this case might be: The counter is mounted, unmounted, and re-mounted, so there are two intervals applying updates to the counter, doubling the rate of the counting. I haven't tried it though, and maybe "remounting" a component with the same id isn't a thing in live view? |
I see. You have a good point about unsubscribing but the issue is that, since the component does not receive messages, it always needs coordination from the parent. Things like apply_interval is also problematic, because IIRC the function runs in a separate process and if it blocks, it becomes a bottleneck. I wonder if your particular could be solved with |
While this is technically possible for us to implement for components, I'm not convinced yet on a valid usecase. As José said, process-based side effects don't fit to be handled here. Thanks! |
Update: I made a new issue to better explain my use case + not revive a dead issue. @josevalim, in my recent issue (#2445) I noted that React had moved away from context because it now provides a way for arbitrary external stores to be accessed in components, and more importantly, for those component's to subscribe to state changes in those external stores. This API is called useSyncExternalStore. While I would certainly like to explore adding such a feature to live view (it's more generic and powerful than a bespoke context API), I think implementing an async version is almost possible today with send_update. The only issue is that send_update has no way to talk to all instances of a given live component. So, live component instances need to register themselves. This actually works fine, but because live components have no unmount callback it is impossible for them to unregister themselves. If this callback were to be implemented, we could have a user space implementation of send_update which broadcasts an update to all instances of a live component that are currently mounted. This would allow a really nice pattern for libraries where they could provide an on_mount lifecycle hook instrument the root live view, and then provide live component(s) which send and receive updates from the root live view. Also, it's not context because it's async and push based. All the state being passed around would be explicit. As of now the only way this could be achieved in user space is, some what ironically, by abusing the JS hooks destroyed callback. Obviously that's a terrible idea, but I might implement it to show how the pattern (not the implementation) can be used. Also, @chrismccord, I'd be interested if you see this as a valid use-case. I think it's better than the original example in that it doesn't have anything to do with trying to treat live components as pseudo-processes. |
I saw in another issue how to find out when the live view itself is unmounted, but I don't see that the same mechanism can be used to know if a component is unmounted.
I'm trying to build the react hooks interface on top of live component and I don't know how to have an effect clean up after itself if I don't know when the component itself is being cleaned up.
The text was updated successfully, but these errors were encountered: