Why isn`t my nextjs button calling a function

43 Views Asked by At

I have a next application for creating animations. The frames are saved onto a timeline where the user can scroll older frames. I am trying to add a simple functionality of deleting one of the frames by clicking an attached button, but even though the syntax is the same as in any other function and there are no errors, the function just isn't called. Here is the component, the parent component, and package.json:

import React from "react";
import { useCanvas } from "./CanvasContext";
import { settings } from "./Signals";
import ReusableLayer from "../components/utility/ResuableLayer";
import { timeline } from "./Signals";
import { v4 as uuidv4 } from "uuid";
import IndexLabel from "../components/utility/IndexLabel";
import JSZip from "jszip";
import { saveAs } from "file-saver";
const Timeline: React.FC = () => {
  const handleDownloadImages = () => {
    const zip = new JSZip();

    // Add each image to the zip file
    timeline.value.forEach((imageDataURL, index) => {
      const base64Data = imageDataURL.split(",")[1];
      zip.file(`image_${index + 1}.png`, base64Data, { base64: true });
    });

    // Generate the zip file and trigger download
    zip.generateAsync({ type: "blob" }).then((blob) => {
      saveAs(blob, "timeline_images.zip");
    });
  };

  const handleDeleteImage = (index: number, e: React.MouseEvent) => {
    e.stopPropagation();
    console.log("click");

    // Create a copy of the timeline array
    const updatedImages = [...timeline.value];

    // Modify the copy
    updatedImages.splice(index, 1);

    // Update the timeline array with the modified copy
    timeline.value = updatedImages;

    console.log("splicing");
  };

  const timelineImageSize = 400

  return (
    <div className="relative w-full overflow-x-auto bg-gray-200">
      <div
        className="flex h-auto overflow-y-hidden flex-row-reverse gap-1 py-1"
        style={{
          width:
            (timelineImageSize) * timeline.value.length - 0
            // settings.value.canvasSize.x,
        }}
      >
        {timeline.value.map((imageDataURL, index) => (
          <div
            className="relative border-2"
            key={uuidv4()}
            style={{
              border: "black 1px solid",
              width:  (timelineImageSize),
              height:  (timelineImageSize),
            }}
          >
            <button
              onClick={(e) => handleDeleteImage(index, e)}
              className="z-50 top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
            >
              Delete
            </button>
            <button
              onClick={(e) => handleDeleteImage(index, e)}
              className=" "
            >
              Delete
            </button>
            <img
              src={imageDataURL}
              width={timelineImageSize} // Render two times smaller
              height={timelineImageSize} // Render two times smaller
              alt={`Image ${index}`}
            />
            {/* <IndexLabel
              label={(index + 1).toString()}
              customClasses="absolute top-0 left-0 text-lg"
            /> */}
          </div>
        ))}
      </div>
      {timeline.value.length > 0 && (
        <button
          // onClick={handleDownloadImages}
          className="mt-4 p-2 bg-blue-500 text-white rounded-md cursor-pointer absolute bottom-0 right-0"
        >
          Download Images
        </button>
      )}
      <button
        onClick={
          (e) => handleDeleteImage(0, e )
         }
        className="z-10   top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
      >
        Delete
      </button>
    </div>
  );
};
 
parent component:
const Page: React.FC = () => {
  const {   canvasRef, frontlineCanvasRef, markerCanvasRef,backgroundCanvasRef } =
    useCanvas();
  const { GlobalData, updateGlobalData } = useGlobalValue();
  const [mouseDownTimeStamp, setMouseDownTimeStamp] = useState<number | null>(
    null
  );
  const [elapsedTime, setElapsedTime] = useState<number>(0);

  const handleMouseDown = (e:  MouseEvent) => {
    if (e.button === 2) {
      e.preventDefault();
    }
    setMouseDownTimeStamp(Date.now());
  };

  const handleMouseUp = (e:  MouseEvent) => {
    e.preventDefault();
    // Using requestAnimationFrame to delay the execution until the next frame
    requestAnimationFrame(() => {
      setElapsedTime(0);
      setMouseDownTimeStamp(null);
    });
  };

  useEffect(() => {
    // Add event listeners when the component mounts
    document.addEventListener("mousedown", handleMouseDown);
    document.addEventListener("mouseup", handleMouseUp, true);

    // Remove event listeners when the component unmounts
    return () => {
      document.removeEventListener("mousedown", handleMouseDown);
      document.removeEventListener("mouseup", handleMouseUp, true);
    };
  }, []);

  useEffect(() => {
    // Update elapsed time while the button is pressed
    if (mouseDownTimeStamp !== null) {
      const intervalId = setInterval(() => {
        setElapsedTime((prevElapsedTime) => prevElapsedTime + 100);
        updateGlobalData("mouseDownTime", elapsedTime + 100); 
      }, 100);

      return () => clearInterval(intervalId);
    }
  }, [mouseDownTimeStamp, elapsedTime]);

  useEffect(() => {
    updateGlobalData("mouseDownTime", elapsedTime); 
  }, [elapsedTime]);

  return (
    <  >   
      <CanvasSettings />
      <CanvasEditor />
      <Timeline />
    </>
  );
};

package.json
{
  "name": "mapping-software",
  "version": "0.1.0",
  "private": true,
  "configurations": [
    {
      "name": "Next: Chrome",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}",
      "sourceMapPathOverrides": {
        "webpack:///./*": "${webRoot}/*"
      }
    }
  ],
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/material": "^5.14.18",
    "@mui/system": "^5.14.18",
    "@preact/signals": "^1.2.1",
    "file-saver": "^2.0.5",
    "html2canvas": "^1.4.1",
    "jszip": "^3.10.1",
    "next": "14.0.0",
    "react": "^18",
    "react-dom": "^18",
    "react-dropzone": "^14.2.3",
    "uuidv4": "^6.2.13"
  },
  "devDependencies": {
    "@types/file-saver": "^2.0.7",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "@types/tinycolor2": "^1.4.6",
    "@wixc3/react-board": "^2.3.0",
    "autoprefixer": "^10",
    "eslint": "^8",
    "eslint-config-next": "14.0.0",
    "postcss": "^8",
    "postcss-preset-env": "^9.2.0",
    "tailwindcss": "^3.3.5",
    "typescript": "^5"
  }
}

The behavior I described is happening only inside the timeline. value.map block. Every button outside it works. Additionally, when I comment out this block it also starts working:

useEffect(() => {
    // Add event listeners when the component mounts
    document.addEventListener("mousedown", handleMouseDown);
    document.addEventListener("mouseup", handleMouseUp, true);

    // Remove event listeners when the component unmounts
    return () => {
      document.removeEventListener("mousedown", handleMouseDown);
      document.removeEventListener("mouseup", handleMouseUp, true);
    };
  }, []);
1

There are 1 best solutions below

0
Nockiest On

for some reason, when I remove the uuidv4 as a keyprop the delete button starts working

{timeline.value.map((imageDataURL, index) => (
      <div
        className="relative border-2"
        key={index} // changed uuidv4() for index
        style={{
          border: "black 1px solid",
          width:  (timelineImageSize),
          height:  (timelineImageSize),
        }}
      >
        <button
          onClick={(e) => handleDeleteImage(index, e)}
          className="z-50  absolute top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
        >
          Delete
        </button>

        <img
          src={imageDataURL}
          width={timelineImageSize}
          height={timelineImageSize}
          alt={`Image ${index}`}
        />
        <IndexLabel
          label={(index + 1).toString()}
          customClasses="absolute top-0 left-0 text-lg"
        />
      </div>
    ))}