Skip to content

Commit

Permalink
Inlined components are cleaned up when unmounted
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmfoley committed Sep 20, 2023
1 parent 0f81754 commit 4c08a63
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
7 changes: 7 additions & 0 deletions isolate-react/src/isolateComponent/nodeTree/reconcile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export const reconcile = (
}

const matchedChildren = matchChildren(previous.children, next.children)
const matchedSet = new Set(matchedChildren.map(([p]) => p).filter((x) => !!x))
const unmatchedChildren = previous.children.filter((p) => !matchedSet.has(p))
for (let unmatchedChild of unmatchedChildren) {
if (unmatchedChild.componentInstance)
unmatchedChild.componentInstance.cleanup()
}

const reconciledChildren = matchedChildren.map(([p, n]) => reconcile(p, n))

if (previous.nodeType === 'html') {
Expand Down
33 changes: 33 additions & 0 deletions isolate-react/test/isolateComponent/inline_effect.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, test } from 'mocha'

import React, { useEffect } from 'react'
import { isolateComponentTree } from '../../src/isolateComponent'
import assert from 'node:assert'

describe('inline effect cleanup', () => {
test('effect cleanup is run for unmounted inlined component', () => {
let calls = [] as string[]

const EffectTracker = ({ tag }: { tag: string }) => {
useEffect(() => {
calls.push(`mount ${tag}`)
return () => {
calls.push(`unmount ${tag}`)
}
}, [])
return <div>{tag}</div>
}

const Wrapper = ({ keys }: { keys: string[] }) => (
<div>
{keys.map((k) => (
<EffectTracker key={k} tag={k} />
))}
</div>
)

const isolated = isolateComponentTree(<Wrapper keys={['a']} />)
isolated.setProps({ keys: [] })
assert.deepEqual(calls, ['mount a', 'unmount a'])
})
})
39 changes: 31 additions & 8 deletions isolate-react/test/isolateHook/waitForUpdate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,44 @@ import { useEffect, useState } from 'react'
import { isolateHook } from '../../src'

describe('waitForUpdate', () => {
const useUpdateImmediate = () => {
const [count, setCount] = useState(0)
it('is called when an update occurs', async () => {
const useUpdateImmediate = () => {
const [count, setCount] = useState(0)

useEffect(() => {
setTimeout(() => setCount((c) => c + 1), 1)
}, [])
useEffect(() => {
setTimeout(() => setCount((c) => c + 1), 1)
}, [])

return count
}
return count
}

it('is called when an update occurs', async () => {
const isolated = isolateHook(useUpdateImmediate)
assert.strictEqual(isolated(), 0)
const updated = await isolated.waitForUpdate()
assert.strictEqual(updated, 1)
assert.strictEqual(isolated.currentValue(), 1)
})

it('is called when an update occurs', async () => {
const useUpdateTwice = () => {
const [count, setCount] = useState(0)

useEffect(() => {
setTimeout(() => setCount((c) => c + 1), 1)
setTimeout(() => setCount((c) => c + 1), 2)
}, [])

return count
}
const isolated = isolateHook(useUpdateTwice)
assert.strictEqual(isolated(), 0)

const first = await isolated.waitForUpdate()
assert.strictEqual(first, 1)
assert.strictEqual(isolated.currentValue(), 1)

const second = await isolated.waitForUpdate()
assert.strictEqual(second, 2)
assert.strictEqual(isolated.currentValue(), 2)
})
})

0 comments on commit 4c08a63

Please sign in to comment.