Состояние возвращает пустой массив даже после установки в useEffect

#javascript #reactjs

Вопрос:

Я видел подобные вопросы в SO, но не смог определить проблему, с которой я сталкиваюсь.

У меня есть переменная useState, которую я установил в useEffect при первой загрузке. Затем, гораздо позже, происходит событие, и в нем я проверяю значение переменной, и это всегда пустой массив []. Однако я добавил второй эффект использования для отслеживания изменений в моей переменной состояния, и я не вижу, где она опустошается. Вот несколько кодов, чтобы подчеркнуть мою проблему:

 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;

 

У меня есть 1 консоль.войдите в систему с эффектом отслеживания файлов. Вот вывод, когда компонент монтируется:

[]

[«хороший.pdf», «verycool.jpg», «удивительный.png», «goodstuff.mp3», «thankyou.doc»]

Это имеет смысл — я инициализирую массив как пустой, затем я немедленно заполняю его значениями, которые затем запускают второй вывод. Однако вот вывод ПОСЛЕ того, как я удаляю файл (это приводит к выполнению handleDrop ()):

[«Новый файл.png»]

Как вы можете видеть, ВСЕ остальные ценности были уничтожены. Мой комментарий в коде «ЗДЕСЬ ФАЙЛЫ = []» демонстрирует, где состояние использования файлов по-прежнему пусто, хотя мой вывод показывает, что оно было заполнено. Эффект использования никогда не выводит пустой массив во второй раз, так где же он опустошается? Я неправильно получаю значение? Мы высоко ценим любую помощь в решении этой загадки. Я решил не показывать код для компонента DragAndDrop, но он кажется надежным и вызывает метод handleDrop в нужное время и с правильными значениями.

Редактировать:

Хотя DragAndDrop кажется надежным, это может быть какой-то проблемой с тем, как я вызываю переданную функцию… или мое использование {детей}. Я не уверен. Итак, вот код для DragAndDrop, который завершает этот компонент.

 
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 amp;amp; 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 amp;amp; 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 amp;amp; (
        <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:

Это потому, что вы мутируете files переменную, а не клонируете ее.

Изменить

 let fileList = files;
 

Для

 const fileList = [...files];
 

Обновить

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

Codesandbox

Комментарии:

1. Спасибо за предложение. Я попробовал это, и результаты те же. Я добавил этот код: const FileList = […файлы]; console.log(список файлов); и вывод, который я получаю, [].

2. Попробуйте изменить const fileList = [...files]; на let fileList = [...files];

3. К сожалению, то же самое происходит и с let.

4. Я изменил свой вопрос, включив в него другой компонент. Я не уверен, виноват ли он, но решил добавить это.

5. @BrianLegg Вы хотите сказать, что при первом handledrop вызове после монтирования компонента files массив console.log в // HERE... комментарии такой же пустой? DragAndDrop Компонент не будет иметь значения, так как он напрямую не взаимодействует с files массивом. Можете ли вы предоставить образец своего кода в codesandbox?