Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting "potentially invalid reference access to a class field via this in a nested function" error

In vanilla JS, my code would work fine. For this case, I'd like to componentize my Wall class which's supposed to display the image in the browser that the user has uploaded. Again, this works normally in vanilla JS but not JSX.

I'm getting a potentially invalid reference access to a class field via this in a nested function on the document.querySelector("#file-input").addEventListener("change", this.previewImages); line which I think is causing the issue.

What am I doing wrong and how can I fix it?

import React, {Component} from 'react';

class Wall extends Component {
    constructor(props) {
        super(props);
        this.previewImages = this.previewImages.bind(this);
    }

    previewImages() {
        let preview = document.createElement("div");

        if (this.files) {
            [].forEach().call(this.files, readAndPreview());
        }

        function readAndPreview() {
            if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
                return alert(file.name + " is not an image");
            }

            let reader = new FileReader();

            reader.addEventListener("load", () => {
                let image = new Image();
                image.height = 100;
                image.title = file.name;
                image.src = this.result;

                let date = Date.now();
                let d = new Date(parseInt(date, 10));
                let ds = d.toString("MM/dd/yy HH:mm:ss");
                console.log(ds);

                let initialCountOfLikes = 0;
                let zeroLikes = document.createElement("h1");
                let zeroLikesTextNode = zeroLikes.createTextNode(initialCountOfLikes + " likes");

                zeroLikes.appendChild(zeroLikesTextNode);

                preview.appendChild(image); // makes image appear
                preview.appendChild(zeroLikes); // makes like count appear

                image.ondblclick = function() {
                    if (initialCountOfLikes === 0) {
                        console.log("Inside if block");
                        initialCountOfLikes++;
                        console.log("initialCountOfLikes++ => " + initialCountOfLikes);
                    } else if (initialCountOfLikes === 1) {
                        console.log("inside second else if block");
                        initialCountOfLikes--;
                        console.log("initialCountOfLikes-- => " + initialCountOfLikes);
                    }
                    zeroLikesTextNode.nodeValue = initialCountOfLikes + " likes";
                };
            });
            reader.readAsDataURL(file);
            document.querySelector("#file-input").addEventListener("change", this.previewImages);
        }
    }

    render() {
        return (
            <div id="file-input-wrapper">
                <input type="file" />
                <label htmlFor="file-input" id={"LblBrowse"} />
            </div>
        );
    }
}

export default Wall;
like image 657
mr test Avatar asked Aug 02 '19 02:08

mr test


2 Answers

The warning is telling you that using this in JavaScript frequently has confusing implications, specifically when used inside a function nested inside another function. this stops referring to the class, and instead refers to the scope of your nested function.

In your case, this probably is a legitimate problem (I think) because you have your class, Wall, which has a method previewImages() and a property files. Within that function, you have instantiated a new function, readAndPreview(), inside which you specify this.previewImages as a function callback to the addEventListener function.

They're saying you're potentially using this.previewImages incorrectly, because you're writing functions in traditional JavaScript syntax, function foo() { ... }, where this keeps being redefined in each child function call. In your case, I believe that this is referring to the context of readAndPreview(), and hence cannot access the method this.previewImages() since this doesn't refer to your parent class, Wall.

People used to do things like, make a var that = this; on the parent class, and you'd know that that always meant the parent class.

But now, with ES6 lambda functions using the "fat arrow" syntax () => { } you can access this and know it's referring to the parent scope.

I believe you can refactor your class to change previewImages() { into previewImages = () => { and know that this will refer to the class. You'll have to do the same with function readAndPreview() {. Change it to const readAndPreview = () => {. If you're setting it to a variable, though, I think you'll have to move it above the place you call it, though. e.g. above

if (this.files) {
    [].forEach().call(this.files, readAndPreview());
}
like image 106
Offlein Avatar answered Sep 18 '22 07:09

Offlein


I faced this error in Angular 8. I used the Arrow function instead of regular functions to solve.

In your case.

readAndPreview = () => { ... }

This might solve your problem.

like image 21
Satya Prakash Avatar answered Sep 18 '22 07:09

Satya Prakash