2

I have seen similar questions on SO but have not been able to identify the issue I'm having.

I have a useState variable that I set in useEffect on first load. Then, at a much later time an event fires and within the event I check the value of the variable and it's always an empty array []. However, I added a second useEffect to track the changes in my state variable and I don't see where it's being emptied. Here's some code to highlight my issue:

import React, { useState, useEffect } from "react";
import { DragAndDrop } from "../components";

const AddKidPage = () => {
  const [files, setFiles] = useState([]);

  useEffect(() => {
    var f = [
      "nice.pdf",
      "verycool.jpg",
      "amazing.png",
      "goodstuff.mp3",
      "thankyou.doc",
    ];

    setFiles(f);
  }, []);

  useEffect(() => {
    console.log(files);
  }, [files]);

  const handleDrop = (newFiles) => {
    let fileList = files;

    // HERE FILES = []

    for (var i = 0; i < newFiles.length; i++) {
      if (!newFiles[i].name) return;
      fileList.push(newFiles[i].name);
    }

    setFiles(fileList);
  };

  return (
    <main>
      <DragAndDrop handleDrop={handleDrop}>
        <div style={{ height: 300, width: 250 }}>
          {files.map((file, i) => (
            <div key={i}>{file}</div>
          ))}
        </div>
      </DragAndDrop>
    </main>
  );
};

export default AddKidPage;

I have 1 console.log in the file-tracking useEffect. Here is the output when the component mounts:

[]

["nice.pdf", "verycool.jpg", "amazing.png", "goodstuff.mp3", "thankyou.doc"]

This makes sense - I'm initializing the array as empty then I immediately fill it with values which then triggers the second output. However, here is the output AFTER I drop a file (this causes handleDrop() to execute):

["newFile.png"]

As you can see ALL other values were wiped out. My comment in code "HERE FILES = []" demonstrates where the files useState is still empty even though my output shows it was filled. The useEffect never outputs an empty array a second time so where is it being emptied? Am I retrieving the value wrong? Any help with this mystery is greatly appreciated. I opted to not show the code for DragAndDrop component but it seems solid and calls the handleDrop method at the right time and with correct values.

EDIT:

Although DragAndDrop seems solid, this could be some sort of issue with how I'm calling the passed in function... or my use of {children}. I'm not sure. So, here is the code for DragAndDrop which completes this component.


import React, { useState, useEffect } from "react";
import styled from "styled-components";

const DragAndDrop = ({ handleDrop: externalDrop, children }) => {
  const [drag, setDrag] = useState(false);
  const [dragCounter, setDragCounter] = useState(0);

  const handleDrag = (e) => {
    e.preventDefault();
    e.stopPropagation();
  };

  const handleDragIn = (e) => {
    e.preventDefault();
    e.stopPropagation();

    setDragCounter(dragCounter + 1);
    if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
      setDrag(true);
    }
  };

  const handleDragOut = (e) => {
    e.preventDefault();
    e.stopPropagation();

    setDragCounter(dragCounter - 1);
    if (dragCounter - 1 === 0) {
      setDrag(false);
    }
  };

  const handleDrop = (e) => {
    e.preventDefault();
    e.stopPropagation();

    setDrag(false);
    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
      externalDrop(e.dataTransfer.files);
      e.dataTransfer.clearData();
      setDragCounter(0);
    }
  };

  useEffect(() => {
    var div = document.getElementById("draganddrop-wrapper");

    div.addEventListener("dragenter", handleDragIn);
    div.addEventListener("dragleave", handleDragOut);
    div.addEventListener("dragover", handleDrag);
    div.addEventListener("drop", handleDrop);

    return () => {
      div.removeEventListener("dragenter", handleDragIn);
      div.removeEventListener("dragleave", handleDragOut);
      div.removeEventListener("dragover", handleDrag);
      div.removeEventListener("drop", handleDrop);
    };
  }, []);

  return (
    <Wrapper id="draganddrop-wrapper">
      {drag && (
        <div className="drag-hover">
          <div className="drag-overlay">
            <div>drop here</div>
          </div>
        </div>
      )}
      {children}
    </Wrapper>
  );
};

const Wrapper = styled.div`
  display: inline-block;
  position: relative;
  .drag-hover {
    border: dashed grey 4px;
    background: rgba(255, 255, 255, 0.8);
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    .drag-overlay {
      position: absolute;
      top: 50%;
      right: 0;
      left: 0;
      text-align: center;
      color: grey;
      font-size: 36px;
    }
  }
`;

export default DragAndDrop;


1 Answer 1

3

Its because you are mutating the files variable instead of cloning it.

Change

let fileList = files;

to

const fileList = [...files];

Update

const handleDrop = useCallback((newFile) => {
    if (newFile.type === "image/png") {
        setFile(newFile.name);
    }
}, []);

Codesandbox

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks for the suggestion. I tried that and the results are the same. I added this code: const fileList = [...files]; console.log(fileList); and the output I get is [].
Try to change const fileList = [...files]; to let fileList = [...files];
Unfortunately the behavior is the same with let.
I've modified my question to include the other component. I'm not sure if it's to blame but figured I'd add it.
@BrianLegg You're saying that the first time handledrop is called after the component mounts, the files array is console.log as empty in the // HERE... comment? The DragAndDrop component would be irrelevant since it doesn't directly interact with files array. Can you provide a sample of your code on codesandbox?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.