1

I'm currently building an Electron app with React, and I keep getting an error that my state is changing while my component isn't mounted.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. in Start (created by Context.Consumer)

I've looked up multiple threads and questions about this, and there are no async tasks or ways that I can possibly think the state is changing in my Start component, though maybe someone else may see something I don't.

I currently only use state in this component to redirect using react router when a file is dropped into the app. Since the Redirect component needs to be rendered in order for the redirect to occur, I have it set to null in my state and then change it to the component when dropped. This way I can simply just put the state down in my return statement for the component.

Store.js:

import React, { useState } from "react";
import { Redirect } from "react-router-dom";
import drop from "../../assets/images/drop.svg";
import { ipcRenderer } from "electron";
import M from "materialize-css";

const Start = () => {
    const [redir, setRedir] = useState(null);

    document.ondragover = document.ondrop = (e) => {
        e.dataTransfer.dropEffect = "copy";
        e.preventDefault();
    };

    document.ondrop = (e) => {
        for (let file of e.dataTransfer.files) {
            // notify user about incompatible file types
            if (
                file.path.split(".").pop() !== "js" &&
                file.path.split(".").pop() !== "css" &&
                file.path.split(".").pop() !== "html" &&
                file.path.split(".").pop() !== "svg"
            ) {
                M.toast({
                    html: `"${file.path
                        .split("/")
                        .pop()}" could not be minified due to incompatible file type.`,
                });
            } else {
                ipcRenderer.send("file:add", file.path);
                setRedir(<Redirect to="/list" />);
            }
        }
        e.preventDefault();
    };

    // display toast for if file contains JSX
    ipcRenderer.on("minify:error-react", (e, data) => {
        M.toast({
            html: `${data.path
                .split("/")
                .pop()} could not be minified because the file contains JSX.`,
        });
    });

    return (
        <div className="start">
            <div className="start-drop">
                <img src={drop} draggable="false" alt="" />
                <p>Drop any HTML, CSS, JS, or SVG files here to minify</p>
            </div>
            {redir}
        </div>
    );
};

export default Start;

As for reference, this is the first page the user sees in the application upon opening. No other component (besides the App component) should be rendering before this one since this is simply just a route. Let me know if you need more code for context and I'll add it.

coddingtonjoel
  • 189
  • 2
  • 12
  • In react you typically add event listeners when the component mounts, and remove them when they unmount. What you have adds/sets the event listeners on each render. – Drew Reese Jun 10 '20 at 05:44
  • can you explain what this line does? `ipcRenderer.send("file:add", file.path);` – Red Baron Jun 10 '20 at 06:43
  • Drew Reese, would that mean I just put all of my drag, drop, and electron listeners inside a useEffect hook? – coddingtonjoel Jun 10 '20 at 20:28
  • And Red Baron ipcRenderer is an Electron import that communicates between the main and renderer processes, similar to a frontend and backend. That line simply sends the file path of a file dropped to the main process, in which the file is dealt with. – coddingtonjoel Jun 10 '20 at 20:30

0 Answers0