When island components are nested, the child island is broken
What version of HonoX are you using?
0.1.15
What steps can reproduce the bug?
Prepare two islands (can be same):
- as parent: An island that accepts children and have something can cause update
- as child: An island that have state and/or effect
Here we have Wrapper component like this:
export default function Wrapper({children, name}: PropsWithChildren<{ name: string; }>) {
const [count, setCount] = useState(0)
const id = useState(() => Math.random().toString(36))[0];
useEffect(() => {
console.log("effect", name, id);
return () => {
console.log("cleanup", name, id);
}
}, [name, id])
return (
<div className={className}>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
{children}
</div>
)
}
https://github.com/honojs/honox/assets/950573/f017a160-6874-4134-b811-1aa81bf18578
reproduce repo is here: https://github.com/berlysia/repro-honox-client-unmount-and-nocleanup
What is the expected behavior?
The number of renders will be minimized, and DOMs that produce the same result will be reused unless a key is given. The effect must disappear properly by calling a cleanup function.
What do you see instead?
Problems:
- The body of the child is evaluated multiple times during the initial render.
- The child unexpectedly remounts when updating the parent. The child's state will reset at this time.
- Cleanup function is not called when unmounting.
So if we have timer or event listeners in the child's effect, they will be over-replicated.
Additional information
No response
Hi @berlysia !!
Thank you for raising the issue. One question. Does this problem happen in a development environment? I've tried it. Indeed, it does not work with HTML and script built with SSG. But it works well on dev server.
Thank you for trying and clarifying.
I did investigation again. In development environment:
- Happened. (unexpected multiple render + does not cleanup)
- To observe this, use
setIntervaltimer and cleanup logic in effect.- With
grandparent -> parent -> childhierarchy, number of renderings are like this:- grandparent: 1
- parent: 2. There is two
setIntervaltimers. - child: 3. There is three timers.
- With
- To observe this, use
- Not happened. (parent state change cause unexpected remount, is not happen)
- It depends.
- For problem 1: Happened, does not cleanup, already described above.
- For problem 2: Not happened. (But I've found another issue about usage of
key)
To summarize issues with nested island components:
- When hydrating the outputted HTML, nested islands are evaluated multiple times without cleanup. The number of evaluation is same as the level of nesting.
- "Problem 1" described in the top of this Issue
- In the production build (not observed in development), updating the state of a parent island causes the state of child islands to reset. It looks like a remount behavior without cleanup.
- "Problem 2" described in the top of this Issue
- When updating the key of a child island to re-initialize explicitly, the part corresponding to the island with the changed key disappears from the screen. During this process, no cleanup occurs, nor is the body of the new child island evaluated.
- This is another issue that I found in https://github.com/honojs/honox/issues/154#issuecomment-2067607031
Hi @berlysia
Thank you for explaining! We should fix them. I'll investigate, but now we have problem #155. I don't know if it is related to this issue, but I'm working on it first.
Hi @berlysia @yusukebe
Sorry, this is probably a bug in "hono/jsx/dom".
I believe https://github.com/honojs/hono/pull/2563 will resolve this issue
I think we can close this now.