Установить состояние массива React после выполнения обещания - PullRequest
2 голосов
/ 31 марта 2020

Я новичок в React и javascript в этом отношении, и у меня проблемы с установкой состояния компонентов. Я пытаюсь получить список фотографий с моей конечной точки. Когда у меня есть список фотографий, мне нужно нанести на карту фотографии и вызвать другую конечную точку, чтобы добавить другие метаданные. Моя цель - установить состояние массива компонентов один раз, чтобы я отображал веб-страницу только один раз со всеми изображениями.

Я считаю, что мне нужен оператор Promise.All, чтобы определить, когда все изображения вернулись. из инструкции извлечения внутри карты. Однако я не уверен, что это правильный подход или как он будет выглядеть.

Я выложу часть своего кода ниже. Любые и все рекомендации приветствуются, спасибо!

сервер. js

const express = require('express');
const app = express();
var AWS = require('aws-sdk');
var url = require('url');
var http = require('http');
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var url = require('url');
var http = require('http');
var sizeOf = require('image-size');
var params = {
    Bucket: "owenpersonalphotos",
};
const URL = "http://owenpersonalphotos.s3.amazonaws.com/";

app.get('/api/listPhotos', (req,res) => {
    console.log("GET /api/listPhotos")
    s3.listObjects(params, function (err, data) {
        res.json(data.Contents)
    });
});

app.get('/api/getPhoto/:key', (req, res) => {
    console.log("GET /api/getPhoto/"+req.params.key)
    let key = req.params.key;
    s3.listObjects(params, function (err, data) {
            let imgUrl = URL+key;
            options = url.parse(imgUrl);
            http.get(options, function (response) {
                var chunks = [];
                response.on('data', function (chunk) {
                  chunks.push(chunk);
                }).on('end', function() {
                  var buffer = Buffer.concat(chunks);
                  res.json(sizeOf(buffer))
                });
            })
    });
});

const port = 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));

Приложение. js

class App extends React.Component {
  constructor() {
    super();
    this.loadPhotos = this.loadPhotos.bind(this);
    this.state = { 
      Photos: []
    };
  }
  componentDidMount() {
    this.loadPhotos();
  }

  loadPhotos() {
    let p = []
    fetch('api/listPhotos')
        .then(res => res.json())
        .then(data => 
          { Promise.all(data.map(data => fetch("api/getPhoto/" + data.Key)
            .then(res => res.json())
            .then(getPhotoData => (p = ({
              src:"https://owenpersonalphotos.s3.amazonaws.com/"+data.Key,
              width: getPhotoData.width,
              height : getPhotoData.height,
              id: data.ETag
            },this.setState({Photos:p}))))))
          })
        }

  render() {
      return (
        <div className="App">
            {this.state.Photos}
        </div>
      );
  }
}

Ответы [ 3 ]

2 голосов
/ 31 марта 2020

Вы можете попробовать что-то в этом роде. К сожалению, я не могу воспроизвести ваш точный сценарий, чтобы он не работал, но вот как вы можете разрешить Promise.all.

const initialiser = fetch('api/listPhotos')
    .then(res => res.json())
    .then(data => {  // map every photo request to the promise of the fetch
        let requests = data.map(data => fetch("api/getPhoto/" + data.Key)
         .then(res => res.json())
         .then(getPhotoData => ({
            src: "https://owenpersonalphotos.s3.amazonaws.com/" + data.Key,
            width: getPhotoData.width,
            height : getPhotoData.height,
            id: data.ETag
        )}))

        // Promise.all waits until all jobs are resolved

        Promise.all(requests)
         .then(responses => this.setState({ Photos: responses });

      })
    })
1 голос
/ 31 марта 2020

Я бы использовал вместо этого React Hooks для функциональных компонентов и сохранения состояния в массиве

import React, { useState, useEffect } from "react";

Состояние, установленное как пустой массив ниже:

const [photos, setPhotos] = useState([]);

Этот следующий эффект будет запускаться getPhotos() один раз при загрузке страницы

 useEffect(() => {
    getPhotos();
  }, []);

getPhotos из API и отображение в состояние photos

const getPhotos = async () => {
    //async way
    try {
        const response = await fetch("YOUR API URL");
        let photos = await response.json;
        setPhotos(photos);
    } catch (err) {
        console.log(err);
    }

       /* with just promises
    return fetch("YOUR API URL")
  .then(response => response.json())
  .then(response => {
    console.log(response);
    setPhotos(response);
  });
  */

, а затем вы можете просто сопоставить его в операторе возврата:

<div className="photos-container">
  {photos.map((p, i) => {
     return (
        <div className="photo" key={i}>
           <img src={photos.imgSrc} alt={photos.name}>
        </div>
     );
   })}
</div>

Надеюсь, это поможет, я знаю, что это не твой пример

0 голосов
/ 31 марта 2020

Ваши выборки были похожи на обещанную адскую ситуацию, я сделаю это немного яснее, как это:


loadPhotos() {
    let promises = [];
    fetch('api/listPhotos')
    .then(res => res.json())
    .then(list => {
        for (let photo of list) {
            const aCall = fetch("api/getPhoto/" + photo.Key).then(res => res.json());
            promises.push(aCall);
        }
        return Promise.all(promises);
    }).then(detailedPhotos => {
        const Photos = detailedPhotos.map(item => ({
            src: "https://owenpersonalphotos.s3.amazonaws.com/" + item.Key,
            width: item.width,
            height : item.height,
            id: item.ETag
        }));
        this.setState({ Photos });
    })
}
render() {
    return (
        <div className="App">
            {this.state.Photos.map(p => <div>{p.id}</div>)}
        </div>
    );
}

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