Почему setCount не увеличивается?

Небольшое приложение для загрузки файлов, при этом я хотел бы получать информацию о ходе выполнения каждого выполненного элемента. Однако setCount не увеличивается каждый раз, когда файл загружается в Firebase, он увеличивается только на единицу каждый раз после завершения всей загрузки.

import { ref, uploadBytesResumable } from "firebase/storage"
import { proStorage } from "../firebase/config"
import { v4 } from "uuid"
import { useState } from "react"

const useUploader = (data, foldername) => {
  const [upLoadcomplete, setuploacomplete] = useState("")
  const [count, setCount] = useState(0)
  const uploadFiles = async () => {
    for (let i = 0; i < data.length; i++) {
      const name = `$${v4()}`
      const location = `${foldername}/${name}`
      const metadata = {
        customMetadata: {
          uuid_name: name,
        },
      }
      const dataRef = ref(proStorage, location)
      const uploadtask = uploadBytesResumable(dataRef, data[i], metadata)

      uploadtask.on(
        "state_changed",
        (snapshot) => {
          const progress =
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          console.info("Upload is " + progress + "% done")
          // eslint-disable-next-line
          switch (snapshot.state) {
            case "paused":
              console.info("Upload is paused")
              break
            case "running":
              console.info("Upload is running")
              setCount( count + 1 )
              break
          }
          setuploacomplete("complete")
        },
        (error) => {
          setuploacomplete("Error with upload try again")
        }
      )
    }
    console.info(upLoadcomplete)
  }
  return [uploadFiles, upLoadcomplete]
}
export default useUploader

Похоже, что setCount должен увеличиваться каждый раз, когда статус меняется на «Выполняется». Но это не так?

Есть идеи

Поскольку обновление состояния не синхронно, может быть, count не увеличился к моменту повторного вызова? Попробуйте вместо этого использовать функцию обновления состояния setCount(prevCount => prevCount + 1)?

evolutionxbox 25.09.2023 11:11

Это вроде как работает, за исключением того, что сначала берется общая сумма для загрузки и добавляются к ней итерации... например, при загрузке 5 файлов сообщается 5, а затем 6,7,8,9,10. Добавление 0,5 работает... но менее идеально.

Brian 25.09.2023 11:47
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем коде вы инициализируете count как 0. Это означает, что этот код:

const [upLoadcomplete, setuploacomplete] = useState("")
const [count, setCount] = useState(0)
const uploadFiles = async () => {
  // ...
  setCount(count + 0)
  // ...
}

фактически становится

const [upLoadcomplete, setuploacomplete] = useState("")
const [count, setCount] = useState(0)
const uploadFiles = async () => {
  // ...
  setCount(0 + 1) // always sets count to 1
  // ...
}

Как описано @evolutionxbox в их комментарии, один из способов обойти эту проблему — вызвать setCount с функцией обратного вызова, которая принимает старое значение и возвращает новое.

const [upLoadcomplete, setuploacomplete] = useState("")
const [count, setCount] = useState(0)
const uploadFiles = async () => {
  // ...
  setCount(oldCount => oldCount + 1) // always adds 1 to the previous value
  // ...
}

Как вы заметили в своем комментарии , пока это работает, вы сталкиваетесь с некоторыми проблемами параллелизма. Когда вы звоните uploadFiles, чтобы начать загрузку, все 5 загрузок запускаются одновременно, и каждая вызывает setCount(oldCount => oldCount + 1). В результате ваш первоначальный счет будет равен 5 (как и ожидалось). Однако для каждого последующего события state_changed, в котором состояние равно running, вы снова увеличиваете этот счетчик (в результате чего count достигает любого числа в зависимости от размера ваших файлов).

Чтобы это исправить, вам нужно звонить setCount только один раз при каждой загрузке.

const useUploader = (data, foldername) => {
  const [upLoadcomplete, setuploacomplete] = useState("")
  const [count, setCount] = useState(0)
  const uploadFiles = async () => {
    for (let i = 0; i < data.length; i++) {
      // ...
      const uploadtask = // ...
      let started = false;

      uploadtask.on(
        "state_changed",
        (snapshot) => {
          const progress =
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          console.info("Upload thread #"+ i + " is " + progress + "% done")
          // eslint-disable-next-line
          switch (snapshot.state) {
            case "paused":
              console.info("Upload is paused")
              break
            case "running":
              console.info("Upload is running")
              if (!started) {
                setCount(oldCount => oldCount + 1)
                started = true;
              }
              break
          }
          // removed: setuploacomplete("complete") (it shouldn't be here)
        },
        (error) => {
          // this upload has encountered an error (but not necessarily all of them)
          setuploacomplete("Error with upload try again")
        },
        () => {
          // this upload has completed (but not necessarily all of them)
        }
      )
    }
  }
  return [uploadFiles, upLoadcomplete];
}

К сожалению, при этом отсчет все равно начнется с 5, а затем уже никогда не изменится. Он также может никогда не установить счетчик, если загрузка завершилась до обработки события state_changed. Если вы хотите обновлять счетчик после завершения каждой загрузки, вместо этого вам следует переместить эту логику в обратный вызов onComplete:

// ...
() => {
  // this upload has completed (but not necessarily all of them)
  setCount(oldCount => oldCount + 1) // increments count, only once the upload finishes
}
// ...

Но теперь у вас есть проблема: count может никогда не достичь длины data, поскольку любая загрузка может завершиться ошибкой. Хотя вы можете добавить ту же самую логику setCount в обработчик ошибок, исправление этих проблем с параллелизмом выходит за рамки этого ответа.

Вместо этого вам лучше создать новый компонент UploadTask, который обрабатывает одну загрузку и соответствующим образом управляет ее промежуточными состояниями. Затем, когда у вас есть массив типа data, вы должны использовать компонент UploadTaskBatch, который порождает один компонент UploadTask для каждой записи в data. Это позволяет вам позволить React выполнять за вас тяжелую работу по обработке одновременных обновлений состояния. Их реализация оставлена ​​в качестве упражнения для читателя.

вау... это очень исчерпывающий ответ, и большое вам спасибо! Я попробую и посмотрю, как пойдет дело! Еще раз большое спасибо за объяснения, я учусь по ходу дела, и очень приятно получить такую ​​помощь.

Brian 25.09.2023 14:59

Другие вопросы по теме