Обратный вызов в S3.putObject (). On ('httpUploadProgress', обратный вызов) не обновляет массив состояний React правильно - PullRequest
0 голосов
/ 03 апреля 2020

Github repo

Моя желаемая цель - массив состояний, который обновляется в режиме реального времени с прогрессом загрузки нескольких файлов.

Каждый обратный вызов может обновляться % прогресса для его файла с правильным индексом в массиве состояний uploadProg, но они как будто не могут видеть обновленный uploadProg, полученный в результате других обратных вызовов.

На изображении ниже первый > (3) [0,0,0] регистрирует uploadProg перед обновлением, второй регистрирует newUploadProg после обновления.

Что мне здесь не хватает? Это неправильное понимание замыканий в Javascript?

Спасибо за любые идеи!

uploadProg state array

import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import AWS from 'aws-sdk';

function App() {
  const [selectedFiles, setSelectedFiles] = useState({})
  const [uploadProg, setUploadProg] = useState([])

  var s3 = new AWS.S3({
    apiVersion: '2012-10-17',
    accessKeyId: 'accessKeyId',
    secretAccessKey: 'secretAccessKey',
  });

   const handleFile = event => {
      setSelectedFiles(event?.target?.files)
      setUploadProg(new Array(Object.keys(event?.target?.files).length).fill(0))
   }

   const upload = event => {
     Object.keys(selectedFiles).forEach((key, index) => {
      console.log(key)
      console.log(selectedFiles[key])
      var params = {
        Body: selectedFiles[key], 
        Bucket: "uploadprogress", 
        Key: `exampleobject${index}`, 
       };

       s3.putObject(params)
        .on('httpUploadProgress', (progressEvent, response) => {
          const newUploadProg = [...uploadProg]
          const percent = parseInt(100*progressEvent.loaded / progressEvent.total)
          newUploadProg[index] = percent
          console.log(uploadProg)
          console.log(newUploadProg)
          console.log('______________')
          setUploadProg(newUploadProg)
        })
        .send((err, data) => {
          if (err) console.log(err, err.stack);
          else     console.log(data);
         })
    })  
   }

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <input type="file" name="file" onChange={handleFile} multiple/>
        <button type="button" class="btn btn-success btn-block" onClick={upload}>Upload</button>
        {uploadProg.map(percent => (<div style={{
          margin: 5,
          width: 100,
          height: 10,
          backgroundColor: 'green'
        }}>
          <div style={{
            height: 8,
            width: percent,
            backgroundColor: 'yellow',
          }}>
          </div>
        </div>))}
      </header>
    </div>
  );
}

export default App;

1 Ответ

0 голосов
/ 03 апреля 2020

Привет тебе / ScottRatigan на r / reactjs:

Что мне здесь не хватает? Является ли это неправильным пониманием замыканий в Javascript?

В некотором роде, да.

Когда несколько событий запускаются быстро и обновляют один и тот же объект, они вызываются с одинаковым начальным значением. Поэтому, если событие 1 обновляет один индекс в массиве, а событие 2 обновляет другой, второе обновление может «стереть» первое обновление. Но поскольку это общая проблема, есть и хорошее решение.

Когда вы вызываете функцию обновления состояния, возвращаемую из useState (в данном случае setUploadProg), вы можете передать ей либо значение, либо функцию обновить значение на основе предыдущего значения. (Это аналогично традиционному вызову setState в компоненте React на основе классов.

Я приведу пример с двумя версиями одной и той же функции:

import React, { useState } from 'react'

export default function Broken() {
  const [arr, setArr] = useState([0, 0]);

  const fn = () => {
    const newArr = [...arr];
    newArr[0] = 1;
    setArr(newArr);
  }

  const fn2 = () => {
    const newArr = [...arr];
    newArr[1] = 2;
    setArr(newArr);
  }

  return (
    <div>
      <button type="button" onClick={() => {
        fn();
        fn2();
      }}>Button</button>
    </div>
  )
}

Приведенный выше блок кода содержит простую версию описанной вами ошибки. Альтернативная версия здесь:

import React, { useState } from 'react'

export default function Works() {
  const [arr, setArr] = useState([0, 0]);

  const fn = () => {
    setArr(arr => {
      arr[0] = 1;
      return arr;
    });
  }

  const fn2 = () => {
    setArr(arr => {
      arr[1] = 2;
      return arr;
    });
  }

  return (
    <div>
      <button type="button" onClick={() => {
        fn();
        fn2();
      }}>Button</button>
    </div>
  )
}

Эта работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...