Changing scalars to render via setActiveScalars

Hello,
I am attempting to change the scalars that are rendered in a vtk.js app but although the setActiveScalars command appears to work (by querying data.getPointData().getScalars().getRange()) my rendered data does not update.

A simple app based on the react-js cone example illustrating my issue is below.
Is this the correct way to change the scalars that are rendered when we have data with multiple data arrays?

In a similar example that I attempted with image data I have the same issue, though the vtkPiecewiseGaussianWidget will update its histogram via:

widget.setDataArray(data.getPointData().getScalars().getData());
widget.render();

App.js

import { useState } from 'react';
import './App.css';
import ShowPolydata from './ShowPolydata.js';
import vtkConeSource      from '@kitware/vtk.js/Filters/Sources/ConeSource';
import vtkDataArray       from '@kitware/vtk.js/Common/Core/DataArray';


function App() {

  const [polydata, setPolydata] = useState(null);

  // Display a cone
  const onShowConeClick = () => {
    const coneSource = vtkConeSource.newInstance({ height: 2, radius: 1, resolution: 80 });
    const polydata = coneSource.getOutputData();

    const N = polydata.getNumberOfPoints();
    var valuesRandom = [];
    for (let i = 0; i < N; i++) {
      valuesRandom[i] = Math.random();
    }
    var valuesB = [];
    for (let i = 0; i < N; i++) {
      valuesB[i] = 0.5;
    }

    const scalarsA = vtkDataArray.newInstance({
      values: valuesRandom,
      numberOfComponents: 1, // number of channels (grayscale)
      dataType: Float32Array, // values encoding
      name: 'scalarsA'
    });
    const scalarsB = vtkDataArray.newInstance({
      values: valuesB,
      numberOfComponents: 1, // number of channels (grayscale)
      dataType: Float32Array, // values encoding
      name: 'scalarsB'
    });
    polydata.getPointData().setScalars(scalarsA);
    polydata.getPointData().addArray(scalarsB);
    setPolydata(polydata);
  }

  const onRefresh = () => {
    setPolydata(null);
  }


  return (
    <div className="App">
      <div id="renderBox">
        {polydata &&
          <div className="App-header">
            <ShowPolydata data={polydata}></ShowPolydata>
          </div>
        }
      </div>
        <div className="App-header">
          <button onClick={onShowConeClick}>Cone</button>
          <button onClick={onRefresh}>Refresh</button>
        </div>
    </div>
  );
}

export default App;

ShowPolyData.js

import { useRef, useEffect, useState } from 'react';

import '@kitware/vtk.js/Rendering/Profiles/Geometry';

import vtkRenderWindow from '@kitware/vtk.js/Rendering/Core/RenderWindow';
import vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
import vtkOpenGLRenderWindow from '@kitware/vtk.js/Rendering/OpenGL/RenderWindow';

import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';

import vtkRenderWindowInteractor from '@kitware/vtk.js/Rendering/Core/RenderWindowInteractor';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';


const ShowPolydata = ({ data }) => {
    const vtkContainerRef = useRef(null);
    const context = useRef(null);
    const [scalarVar, setScalar] = useState('scalarsA')

    useEffect(() => {
        if ((!context.current) && (data)) {
            //
            const openglRenderWindow = vtkOpenGLRenderWindow.newInstance();
            const renderWindow = vtkRenderWindow.newInstance();
            const renderer = vtkRenderer.newInstance({ background: [0, 0, 0], });
            renderWindow.addRenderer(renderer);

            // mapper - add to actor
            const mapper = vtkMapper.newInstance();
            const actor = vtkActor.newInstance();
            const lookupTable = vtkColorTransferFunction.newInstance();
            lookupTable.applyColorMap(vtkColorMaps.getPresetByName('jet'));
            lookupTable.updateRange();

            mapper.setInputData(data);
            mapper.setScalarModeToDefault();
            mapper.setScalarRange(0, 1);
            mapper.setLookupTable(lookupTable);

            actor.setMapper(mapper);
            actor.getProperty().setAmbient(1.0);
            actor.getProperty().setDiffuse(0.9);

            renderer.addActor(actor);

            renderWindow.addView(openglRenderWindow);
            openglRenderWindow.setSize(600, 500);
            openglRenderWindow.setContainer(vtkContainerRef.current)

            // Setup interactor and style
            const interactor = vtkRenderWindowInteractor.newInstance();
            interactor.setView(openglRenderWindow);
            interactor.initialize();
            interactor.bindEvents(vtkContainerRef.current);
            interactor.setInteractorStyle(vtkInteractorStyleTrackballCamera.newInstance());

            // all done - reset camera and render
            renderer.resetCamera();
            renderWindow.render();

            context.current = {
                openglRenderWindow,
                renderWindow,
                renderer,
                data,
                actor,
                mapper,
            };
        }

        return () => {
            if (context.current) {
                const { openglRenderWindow, actor, mapper } = context.current;
                actor.delete();
                mapper.delete();
                openglRenderWindow.delete();
                context.current = null;
            }
        };
    }, [vtkContainerRef]);

    function resetCamera() {
        if (context.current) {
            context.current.renderer.resetCamera();
            context.current.renderWindow.render();
        }
    };

    useEffect(() => {
        if (context.current) {
            const { data, mapper, renderWindow } = context.current;
            data.getPointData().setActiveScalars(scalarVar);
            const lut = mapper.getLookupTable();
            lut.updateRange();
            console.log("scalar now: ", scalarVar, 'range: ', data.getPointData().getScalars().getRange())
            renderWindow.render();
        }
    }, [scalarVar]);


    return (
        <div>
            <div ref={vtkContainerRef} ></div>
            <div id="buttonRow" className="viewA-buttons">
                <button onClick={resetCamera}>
                    Reset Camera
                </button>
                <select value={scalarVar} style={{ width: '100%' }} onInput={(ev) => setScalar(ev.target.value)}>
                    <option value="scalarsA">scalarsA</option>
                    <option value="scalarsB">scalarsB</option>
                </select>
            </div>
        </div>
    );
}

export default ShowPolydata;

Thanks for any help / advice.

Quick follow up to this, error pretty clear in the end.
Needed to update the useEffect to fire on a scalarVar state change and then improve the cleanup and rerender.
Further steps not included would be to save camera state so that a scalar change will not reset the camera as well - depends on use case I guess.

So, new “ShowPolydata.js”:

import { useRef, useEffect, useState } from 'react';

import '@kitware/vtk.js/Rendering/Profiles/Geometry';

import vtkRenderWindow from '@kitware/vtk.js/Rendering/Core/RenderWindow';
import vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
import vtkOpenGLRenderWindow from '@kitware/vtk.js/Rendering/OpenGL/RenderWindow';

import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';

import vtkRenderWindowInteractor from '@kitware/vtk.js/Rendering/Core/RenderWindowInteractor';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';


const ShowPolydata = ({ data }) => {
    const vtkContainerRef = useRef(null);
    const context = useRef(null);
    const scalars = [{ name: 'scalarsA', arrayName: 'scalarsA' },
    { name: 'scalarsB', arrayName: 'scalarsB' },]
    const [scalarVar, setScalarVar] = useState(scalars[1].arrayName)

    useEffect(() => {
        if ((!context.current) && (data)) {
            //
            const openglRenderWindow = vtkOpenGLRenderWindow.newInstance();
            const renderWindow = vtkRenderWindow.newInstance();
            const renderer = vtkRenderer.newInstance({ background: [0, 0, 0], });
            renderWindow.addRenderer(renderer);

            // Update data scalar variable
            data.getPointData().setActiveScalars(scalarVar);

            // mapper - add to actor
            const mapper = vtkMapper.newInstance();
            const actor = vtkActor.newInstance();
            const lookupTable = vtkColorTransferFunction.newInstance();
            lookupTable.applyColorMap(vtkColorMaps.getPresetByName('jet'));
            lookupTable.updateRange();

            mapper.setInputData(data);
            mapper.setScalarModeToDefault();
            mapper.setScalarRange(0, 1);
            mapper.setLookupTable(lookupTable);

            actor.setMapper(mapper);
            actor.getProperty().setAmbient(1.0);
            actor.getProperty().setDiffuse(0.9);

            renderer.addActor(actor);

            renderWindow.addView(openglRenderWindow);
            openglRenderWindow.setSize(600, 500);
            openglRenderWindow.setContainer(vtkContainerRef.current)

            // Setup interactor and style
            const interactor = vtkRenderWindowInteractor.newInstance();
            interactor.setView(openglRenderWindow);
            interactor.initialize();
            interactor.bindEvents(vtkContainerRef.current);
            interactor.setInteractorStyle(vtkInteractorStyleTrackballCamera.newInstance());

            // all done - reset camera and render
            renderer.resetCamera();
            renderWindow.render();

            context.current = {
                openglRenderWindow,
                renderWindow,
                renderer,
                data,
                actor,
                mapper,
            };
        }

        return () => {
            if (context.current) {
                const { openglRenderWindow, actor, mapper, renderWindow, renderer } = context.current;
                actor && actor.delete();
                mapper && mapper.delete();
                openglRenderWindow && openglRenderWindow.delete();
                renderWindow && renderWindow.delete();
                renderer && renderer.delete();
                context.current = null;
                if (vtkContainerRef.current) {
                    vtkContainerRef.current.innerHTML = "";
                }
            }
        };
    }, [vtkContainerRef, data, scalarVar]);

    function resetCamera() {
        if (context.current) {
            context.current.renderer.resetCamera();
            context.current.renderWindow.render();
        }
    };

    const swapArray = (event) => {
        if (scalarVar !== event.target.value) {
            setScalarVar(event.target.value);
        }
    }

    return (
        <div>
            <div ref={vtkContainerRef} ></div>
            <div id="buttonRow" className="viewA-buttons">
                <button onClick={resetCamera}>
                    Reset Camera
                </button>

                <tbody>
                    <tr>
                        <td><input type="radio"
                            value={scalars[0].arrayName}
                            checked={scalarVar === scalars[0].arrayName}
                            onChange={swapArray} />{scalars[0].name}</td>
                        <td><input type="radio"
                            value={scalars[1].arrayName}
                            checked={scalarVar === scalars[1].arrayName}
                            onChange={swapArray} />{scalars[1].name}</td>
                    </tr>
                </tbody>
            </div>
        </div>
    );
}

export default ShowPolydata;