在React.js应用上从firebase中获取和显示图片。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在React.js应用上从firebase中获取和显示图片。相关的知识,希望对你有一定的参考价值。

所以我试图从Firebase存储中获取图片,然后在React组件中显示它们。坦率地说,我是Reactjavascript的新手,我很难理解ReactJS的异步性,我遇到的问题是,我的图片被获取了,但组件没有在获取完成后重新渲染......

这是我主页面上的代码,在useEffect函数中,我试图获取图片,然后将它们的下载URL存储在一个数组中,然后将该数组设置为状态(在页面加载时,即只有一次)。由于这些都是承诺,所以它不是同步发生的,当我第一次加载页面时,它没有显示任何图片。然而,如果我点击页面上的其他组件,它就会重新渲染我的Pictures组件,并且图像会出现(??),所以我知道取图成功了。

    let storageRef = firebase.storage().ref()
    let calendarRef = React.createRef()

    const position = props.location.state.name.indexOf("@")
    const username = props.location.state.name.substring(0, position);

    const [simpleDate, setSimpleDate] = useState(null)
    const [selectedDate, setSelectedDate] = useState('')
    const [showModal, setShowModal] = useState(false)
    const [showCancelModal, setShowCancelModal] = useState(false)
    const [expectedPeople, setExpectedPeople] = useState(null)
    const [events, setEvents] = useState([])
    const [helpModal, showHelpModal] = useState(false)

    const [pictureURLs, setPictureURLs] = useState([])


    useEffect(() => 

        //load pictures
        const fetchImages = async () => 
            let urls = []
            storageRef.child('cabinPictures').listAll().then((result) => 
                result.items.forEach((imageRef) => 
                    imageRef.getDownloadURL().then(url => 
                        urls.push(url)
                    )
                )
            )
            return urls;
        

        fetchImages().then(urls => 
            setPictureURLs(urls);
            console.log("inside .then() " + pictureURLs)
        )


        //fetch reservations
        firebase
            .firestore()
            .collection('reservations')
            .onSnapshot(serverUpdate => 
                const reservations = serverUpdate.docs.map(_doc => 
                    const data = _doc.data();
                    data['id'] = _doc.id;
                    return data;
                );

                let fetchedEvents = reservations.map(reservation => 
                        let date = reservation.reservationDate.toDate()
                        const month = ("0" + (date.getUTCMonth() + 1))
                        let dateString = date.getUTCFullYear() + "-" + ("0" + (date.getUTCMonth()+1)).slice(-2) + "-" + ("0" + date.getUTCDate()).slice(-2)
                        return title: reservation.username + " - " + reservation.numPeople + " total", date: dateString, id: reservation.id, totalPeople: reservation.numPeople, month: month
                    )
                console.log(fetchedEvents)
                setEvents(fetchedEvents)
            );
    , [])    

我的Pictures组件在主页面中的使用效果(上图)运行。我把state中的urls作为一个道具传递过来。

<div className="pictures-div-container">
      <Pictures pictureURLs=pictureURLs>

      </Pictures>
 </div>

我的Picture组件的代码

import React,  useState, useEffect  from 'react'
import styles from "./styles.css"

const firebase = require('firebase');


const Pictures = (props) => 

    const [uploadImage, setUploadImage] = useState(null)
    const [progressValue, setProgressValue] = useState(0)

    let storageRef = firebase.storage().ref()
    let  pictureURLs  = props


    const handleUpload = () => 
        setProgressValue(0)

        const uploadTask = storageRef.child(`cabinPictures/$uploadImage.name`).put(uploadImage)
        uploadTask.on('state_changed', 
        (snapshot) => 
            //progress function
            const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes ) * 100)
            setProgressValue(progress)
        , 
        (error) => 
            //error function
            console.log(error)
        ,
        () => 
            //complete function
            storageRef.child('cabinPictures').child(uploadImage.name).getDownloadURL().then(url => 
                console.log(url)
             )
        );
    

    const handleFileSelect = (e) => 
        if (e.target.files[0]) 
            setUploadImage(e.target.files[0])
        
    

    return (
        <div className="pictures-container">
            <h2>Upload a Picture!</h2>
            <button className="upload-button" onClick=() => handleUpload()>Upload</button>
            <input type="file" onChange=(e) => handleFileSelect(e)></input>
            <progress value=progressValue max="100"></progress>
            <div className="pictures"> 
                
                    pictureURLs.map((url, index) => 
                        return <img className="picture" key=index src=url></img>

                    )
                
            </div>
        </div>
    )


export default Pictures

那么,谁能帮我理解一下,为什么Pictures组件在从firebase获取图片urls后,当状态被设置时,不会自动重新渲染?我以为当一个组件中的道具发生变化时,整个组件都会重新渲染?

EDIT:于是我就按照答案的建议修改了我的主页面的useEffect函数,果然成功了!

        //fetch and load pictures
        const fetchImages = async () => 

            let result = await storageRef.child('cabinPictures').listAll();
            let urlPromises = result.items.map(imageRef => imageRef.getDownloadURL())

            return Promise.all(urlPromises)
        

        const loadImages = async () => 
            const urls = await fetchImages()
            setPictureURLs(urls)
        

        loadImages()
答案

你必须让所有的嵌套承诺解决之前,你必须让所有的承诺解决。return urls

我对firebase的API不是很了解,所以不确定是否会有 result.items 是一个实际的数组或一个有foreach方法的对象。如果它是一个js数组,下面的代码应该是有效的

试试这样的事情。

//load pictures
const fetchImages = async() => 

  let result = await storageRef.child('cabinPictures').listAll();
  /// map() array of the imageRef.getDownloadURL() promises 
  let urlPromises = result.items.map(imageRef => imageRef.getDownloadURL());

  // return all resolved promises
  return Promise.all(urlPromises);


const urls = await fetchImages()
setPictureURLs(urls);
console.log("inside .then() ", urls) 

以上是关于在React.js应用上从firebase中获取和显示图片。的主要内容,如果未能解决你的问题,请参考以下文章

React JS App 与 Firebase 远程配置以获取远程标志

是否有使用 react.js 和 firebase 的简单 CRUD 示例应用程序?

如何从 React JS 中的 firebase 文档的所有子集合中获取数据? [复制]

使用 node.js 和 react.js 客户端启动一个 firebase 功能项目

Firebase react.js 应用部署 - 空白页面

在 React.JS 中刷新后,如何让用户保持登录到 firebase?