import { h, render, mount, nextTick, type WDom } from '@/index';
// Provider 컴포넌트 - children을 props로 받아서 렌더링
const Provider = mount((renew, _props, children: WDom[]) => {
let k = 0;
return () => (
);
});
// Child 컴포넌트 - 자체 상태를 가지고 있음
const Child = mount<{ id: string }>(renew => {
let v = 0;
return props => (
Child {props.id}
);
});
// 테스트 컴포넌트 - Provider에 children을 props로 전달
const TestComponent = mount(() => {
return () => (
);
});
const testWrap = document.getElementById('root');
if (testWrap) {
render(, testWrap);
}
if (import.meta.vitest) {
const { it, expect, describe } = import.meta.vitest;
describe('Children disappearing issue reproduction', () => {
it('Children should not disappear when parent renews after child renews', async () => {
const container = document.createElement('div');
render(, container);
await nextTick();
// 초기 상태 확인
const childA = container.querySelector('.child-A');
const childB = container.querySelector('.child-B');
const providerBtn = container.querySelector(
'.provider-btn'
) as HTMLButtonElement;
const childABtn = container.querySelector(
'.child-A .child-btn'
) as HTMLButtonElement;
expect(childA).not.toBeNull();
expect(childB).not.toBeNull();
expect(childABtn?.textContent).toContain('v = 0');
console.log('[TEST] Step 1: Initial state - children visible');
childABtn?.click();
await nextTick();
console.log(
'[TEST] Step 2: Child A renewed - checking children still visible'
);
const childAAfterRenew = container.querySelector('.child-A');
const childBAfterRenew = container.querySelector('.child-B');
const childABtnAfter = container.querySelector(
'.child-A .child-btn'
) as HTMLButtonElement;
expect(childAAfterRenew).not.toBeNull();
expect(childBAfterRenew).not.toBeNull();
expect(childABtnAfter?.textContent).toContain('v = 1');
// Step 2: Provider의 버튼 클릭 (k = 0 → k = 1)
providerBtn?.click();
await nextTick();
console.log(
'[TEST] Step 3: Provider renewed - checking children still visible'
);
const childAFinal = container.querySelector('.child-A');
const childBFinal = container.querySelector('.child-B');
const childABtnFinal = container.querySelector(
'.child-A .child-btn'
) as HTMLButtonElement;
const providerBtnFinal = container.querySelector(
'.provider-btn'
) as HTMLButtonElement;
// 핵심 검증: Provider가 renew된 후에도 children이 사라지지 않아야 함
expect(childAFinal).not.toBeNull();
expect(childBFinal).not.toBeNull();
expect(childABtnFinal?.textContent).toContain('v = 1'); // Child의 상태도 유지되어야 함
expect(providerBtnFinal?.textContent).toContain('k=1');
console.log(
'[TEST] Test completed - children preserved after parent renew'
);
});
it('Multiple child renews followed by parent renew should preserve children', async () => {
const container = document.createElement('div');
render(, container);
await nextTick();
const providerBtn = container.querySelector(
'.provider-btn'
) as HTMLButtonElement;
const childABtn = container.querySelector(
'.child-A .child-btn'
) as HTMLButtonElement;
const childBBtn = container.querySelector(
'.child-B .child-btn'
) as HTMLButtonElement;
// Child A를 2번 renew
childABtn?.click();
await nextTick();
childABtn?.click();
await nextTick();
// Child B를 1번 renew
childBBtn?.click();
await nextTick();
expect(
container.querySelector('.child-A .child-btn')?.textContent
).toContain('v = 2');
expect(
container.querySelector('.child-B .child-btn')?.textContent
).toContain('v = 1');
// Provider renew
providerBtn?.click();
await nextTick();
const childAFinal = container.querySelector('.child-A');
const childBFinal = container.querySelector('.child-B');
const childABtnFinal = container.querySelector(
'.child-A .child-btn'
) as HTMLButtonElement;
const childBBtnFinal = container.querySelector(
'.child-B .child-btn'
) as HTMLButtonElement;
expect(childAFinal).not.toBeNull();
expect(childBFinal).not.toBeNull();
expect(childABtnFinal?.textContent).toContain('v = 2');
expect(childBBtnFinal?.textContent).toContain('v = 1');
});
it('Direct slot children pattern should work correctly', async () => {
const SlotProvider = mount(renew => {
let k = 0;
return () => (
Slot Provider
);
});
const container = document.createElement('div');
render(, container);
await nextTick();
const childXBtn = container.querySelector(
'.child-X .child-btn'
) as HTMLButtonElement;
const providerBtn = container.querySelector(
'.slot-provider-btn'
) as HTMLButtonElement;
// Child X renew
childXBtn?.click();
await nextTick();
expect(
container.querySelector('.child-X .child-btn')?.textContent
).toContain('v = 1');
// Provider renew
providerBtn?.click();
await nextTick();
// Slot 패턴에서는 정상 동작해야 함
expect(container.querySelector('.child-X')).not.toBeNull();
expect(container.querySelector('.child-Y')).not.toBeNull();
expect(
container.querySelector('.child-X .child-btn')?.textContent
).toContain('v = 1');
});
});
}