useRef: state becomes undefined after rendering

352 Views Asked by At

I'm using ml5.js to train an ML model. I have to add images to the model using the webcam. The train function works fine with the current code. However, when I try to set a state within my if else statement inside the train function, it throws an error when I try to test it using the test button.

the value of classifier becomes undefined.

export const Component: React.FC<ComponentProps> = (props: ComponentProps) => {
    let classifier: any;
         classifier = featureExtractor.classification(capture, videoReady);
    }

    
    function final() {
        classifier.classify(capture, (error, result) => {
            setPrediction(label);
            
        });
    }

    return (<div>
            <Button variant="contained" color="primary" onClick={() => final()}>testing!</Button>
        </div>
    </div>)
        ;
};

classifier is a variable so it would re-render each time. useRef could be used here but I couldn't figure out how.

const classifier = useRef()
classifier.current

accessing it like this still gave me the error. I also tried making a state for the classifier itself but that did not seem to work for me either.

Here's a Codesandbox to try the full thing. To see the error, you can set a state in the if else statement of the train function:

https://codesandbox.io/s/hardcore-solomon-zb34l?file=/src/Component.tsx

2

There are 2 best solutions below

0
On BEST ANSWER

I've provided a forked version of the Codesandbox that was mentioned above in the comments: https://codesandbox.io/s/frosty-sea-0pcfx?file=/src/Component.tsx. This contains a few fixes, but most of it related to changing the local variables for capture and classifier and assigning them to refs. The following code:

let capture: p5Types.Element;
let classifier: any;

was changed to:

let capture: any = useRef<any>();
let classifier: any = useRef<any>();

Then all references to those variables in the other areas of the code were switched to capture.current and classifier.current. Those could potentially be stored in state as well, but they appear to only be assigned and utilized outside of data used during rendering and don't require the component to re-render whenever they are assigned.

0
On

I would do:

const { current: heap } = useRef({});

// In your `setup`
heap.classifier = featuresExtractor.classification(..);

// elsewhere access it as
heap.classifier

when you wrote:

const classifier = useRef()
classifier.current

classifier.current is persistent across re-renders, but you still need to asign it in setup as classifier.current = .... I prefer the way I wrote, because heap becomes a convenient place to add any other stuff which should be persistent across re-renders.