You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
use_effect's unmount method is not always called with dynamically rendered children #1198
I created a component that simulates a list of conditionally rendered children. Each child in the list contains a unique key. However, the use_effect unmount method does not always appear to be called when the component is dismounted.
Here is a minimum viable example:
fromuuidimportuuid4fromreactpyimportcomponent, hooks, html@componentdefchild():
ownership_uuid=hooks.use_memo(lambda: uuid4().hex)
@hooks.use_effect(dependencies=[])asyncdefunmount_manager():
print("registering new unmount func")
defunmount():
# FIXME: This is not working as expected. Dismount is not always called.print("unmount func called")
returnunmountreturnownership_uuid@componentdefroot():
components=hooks.use_ref([child(key=uuid4().hex)])
reload_trigger, set_reload_trigger=hooks.use_state(True)
asyncdefadd_front_child(event):
components.current.insert(0, child(key=uuid4().hex))
set_reload_trigger(notreload_trigger)
asyncdefadd_end_child(event):
components.current.append(child(key=uuid4().hex))
set_reload_trigger(notreload_trigger)
asyncdefremove_front_child(event):
ifcomponents.current:
components.current.pop(0)
set_reload_trigger(notreload_trigger)
asyncdefremove_end_child(event):
ifcomponents.current:
components.current.pop()
set_reload_trigger(notreload_trigger)
returnhtml.div(
html.div(
html.button({"onClick": add_front_child}, "Add End"),
html.button({"onClick": add_end_child}, "Add Front"),
html.button({"onClick": remove_front_child}, "Remove End"),
html.button({"onClick": remove_end_child}, "Remove Front"),
),
html.div(f"Components: {components.current}"),
components.current,
)
You can demo this by adding a bunch of children, then removing them. If you look at terminal, you'll notice that unmount was only called once despite multiple children being unloaded.
This might be related to a bug I've seen where nested children are marked as "rendered" and then if there's subsequent updates to their state they get skipped. Changing the rendering order can expose or hide the bug.
If I had to guess, an example of this:
Button alters state on child and triggers callback on parent
Parent alters parameter passed to child based on callback
Child doesn't render with new parameter because it was already rendered.
This can create a case where subsequent triggers to the child fail because the component is unmounted
I'm writing this off the top of my head, I don't have a reproducible state off hand.
I don't have any free time unfortunately. I'm also not sure the sure the use case for this. It seems like component rendering shouldn't have side effects. If the cause is the example I referred to, the solution would be rewriting the render logic, which I believe you already intend to do.
Activity
JamesHutchison commentedon Nov 3, 2024
This might be related to a bug I've seen where nested children are marked as "rendered" and then if there's subsequent updates to their state they get skipped. Changing the rendering order can expose or hide the bug.
If I had to guess, an example of this:
Button alters state on child and triggers callback on parent
Parent alters parameter passed to child based on callback
Child doesn't render with new parameter because it was already rendered.
This can create a case where subsequent triggers to the child fail because the component is unmounted
I'm writing this off the top of my head, I don't have a reproducible state off hand.
Archmonger commentedon Dec 13, 2024
@JamesHutchison Would you be interested at taking a crack at this issue?
JamesHutchison commentedon Dec 13, 2024
I don't have any free time unfortunately. I'm also not sure the sure the use case for this. It seems like component rendering shouldn't have side effects. If the cause is the example I referred to, the solution would be rewriting the render logic, which I believe you already intend to do.
v2.0.0b5 (reactive-python#1312)