Skip to content

Commit a3942cd

Browse files
committed
fix(Autofocus): guard depthPickingPass/copyPass against double dispose
EffectComposerImpl.dispose() disposes every pass it holds, including these two added via composer.addPass - if Autofocus unmounts alongside its ancestor EffectComposer, its own cleanup disposed them a second time. This collision only becomes reachable now that the composer lifecycle fix (previous commit) actually disposes the composer on unmount - previously it leaked and never disposed at all, so it never collided with Autofocus's own cleanup.
1 parent 7df1c67 commit a3942cd

1 file changed

Lines changed: 19 additions & 2 deletions

File tree

src/effects/Autofocus.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ import { Mesh, Vector3 } from 'three'
1818
import { EffectComposerContext } from '../EffectComposer'
1919
import { DepthOfField } from './DepthOfField'
2020

21+
// EffectComposerImpl.dispose() disposes every pass it currently holds —
22+
// including these two, since they're added via composer.addPass below.
23+
// When Autofocus unmounts alongside its ancestor EffectComposer (e.g. a
24+
// full tree unmount), both the composer's own teardown AND this
25+
// component's cleanup effect would dispose the same instances. Wrapping
26+
// dispose here makes it safe no matter which caller gets there first.
27+
function makeDisposeIdempotent<T extends { dispose: () => void }>(instance: T): T {
28+
let disposed = false
29+
const dispose = instance.dispose.bind(instance)
30+
instance.dispose = () => {
31+
if (disposed) return
32+
disposed = true
33+
dispose()
34+
}
35+
return instance
36+
}
37+
2138
export type AutofocusProps = ComponentProps<typeof DepthOfField> & {
2239
target?: R3FVector3
2340
/** should the target follow the pointer */
@@ -55,8 +72,8 @@ export function Autofocus({
5572
const { composer, camera } = useContext(EffectComposerContext)
5673

5774
// see: https://codesandbox.io/s/depthpickingpass-x130hg
58-
const [depthPickingPass] = useState(() => new DepthPickingPass())
59-
const [copyPass] = useState(() => new CopyPass())
75+
const [depthPickingPass] = useState(() => makeDisposeIdempotent(new DepthPickingPass()))
76+
const [copyPass] = useState(() => makeDisposeIdempotent(new CopyPass()))
6077
useEffect(() => {
6178
composer.addPass(depthPickingPass)
6279
composer.addPass(copyPass)

0 commit comments

Comments
 (0)