React Example, useEffect return not clearing resize listener


this may be a silly question, but I just started using vtk.js and tried implementing this React example ( Using vtk.js with React | vtk.js ). It includes unmount clear routine on useEffect, so I added conditional mounting, and… the ‘resize’ event listener calling publicApi.resize() is not getting removed. On subsequent mounts the component creates more and more listeners + resizing throws errors, as it tries to calculate dims using model.container, even though publicApi that the listener callback uses and it’s model.container are deleted.

Here is it on codesandbox. It is the same example except vtkContainerRef.current is defined as vtkFullScreenRenderWindow’s container, not rootcontainer, so it doesn’t take the entire window (maybe I should use different function if I don’t want to have it on full screen?).

So is the example’s routine not correct? Am I doing something wrong? I assume the solution is quite simple, but I could not find more examples that use React other than this one.

I’m not sure if I should add something to the useEffect or if FullscreenRenderWindow’s publicAPI should remove the listener when it’s deleted. I tried replacing FullscreenRenderWindow with GenericRenderWindow, which has .unbindEvents() method, but

  1. it only unbinds key presses
  2. Unbinding events crashes, because it uses RenderWindowInteractor’s own model.container instead of the called argument and GenericRenderWindow calls model.interactor.unbindEvents(model.container).

While this is not the answer, you should not use fullScreenRenderer when you don’t want your render window to be fullscreen. You should use a proper renderwindow on which you should call setContainer().

Okay, I got past the initial problem by replacing newInstance() with extend(apiRef, modelRef) and manually removing ‘resize’ on useEffect’s return, since newInstance() return does not have .resize definition on it. It’s the same case for GenericRenderWindow, as it also won’t remove it’s event listener on deletion, but at least the callback won’t be doing anything.

Anyway, I would like to point out that that GenericRenderWindow should not get a container inside initialConditions, even though the typescript interface suggest it’s valid or at least there should be a note somewhere that it requires to bind events on the Interactor manually.

This is the interface:

RenderWindowInteractor updates it’s own model.container reference on BindEvents.
And this reference is used when unbinding

GenericRenderWindow on unbindEvents tries to pass it’s own model.container.

But if you skip .setContainer by setting it up inside InitialValues, then:

  • setCointainer will have never been called,
  • so bindEvents will have never been called,
  • so RenderWindowInteractor’s model.container wasn’t updated,
  • so it will still be null
  • so when you call setContainer next time (e.g. during .delete() ) it will error on unbinding events, since RenderWindowInteractor will try to call .removeEventListener on it’s null model.container.

So if you want to have container as the initialValue, you’d have to bindEvents with the interactor manually, but I don’t think it’s implied anywhere on the docs? And since all examples use vtkFullScreenWindowRenderer, which binds them on initialization, it’s quite a time dump for anyone new with the VTK to dig through the source code to find where the problem is.