在 Firebase 存储中上传并获取 link 以将其添加到实时数据库中

Upload in Firebase Storage and fetch link to add it in Realtime Database

我在这个应用程序中使用了 Firebase 实时数据库和 Firebase 存储,目标是在 Firebase 存储中上传 pictures 数组中的图像,然后获取 Firebase 的 link存储到该图像并将其添加到将在 imagesUriArray 中推送并添加到实时数据库的对象中。问题是当我按下 addItem 它成功更新了 id,但 images 参数仍然为空。事实上,除非我刷新屏幕,否则 imagesUriArray 仍然是空的。

 export default function NewItem({ route, navigation }) {

    const [pictures, setPictures] = useState([]);
    const [imagesUriArray, setImageUriArray] = useState([]);

    useEffect(() => {
        navigation.setOptions({
            headerRight: () => (
                <TouchableOpacity onPress={addItem}>
                    <Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
                </TouchableOpacity>
            )
        })
    })
    

    const addItem = async () => {

            uploadImages()

            const changes = ref(db, path)
                get(changes).then(async (snapshot) => {
                    if (snapshot.val().data !== undefined) {
                        const fetchedArray = snapshot.val().data

                        let array = fetchedArray;

                        let object = {
                            "id": `${Math.random()}`,
                            "images": imagesUriArray,
                        }

                        array.push(object)
                    
                        update(changes, {
                            data: array
                        })
                    
                    }
                })
        }
    }

    const uploadImages = () => {

        const metadata = {
            contentType: 'image/jpeg',
        };


        pictures.forEach( async (obj) => {
            const id = obj.id
            const uri = obj.uri

            const response = await fetch(uri);
            const blob = await response.blob();
            var ref = storageUpload(storage, path)
            await uploadBytes(ref, blob, metadata)

            await getDownloadURL(ref)
            .then((metadata) => {
                let array = imagesUriArray
                    
                let object = {
                    "id": id,
                    "uri": metadata
                }

                array.push(object)
                setImageUriArray(array)
            })
            .catch((error) => {
                console.log(error)
            });
        })
    }

  return(
     ..............
  )
}

问题

这似乎是 uploadImages 回调中的状态突变。对 imagesUriArray 状态的引用在本地缓存,变异( 即直接推入数组 ),然后将相同的引用保存回状态。这不会触发对任何更新的状态值重新呈现的反应。

const uploadImages = () => {
  ...
  
  pictures.forEach( async (obj) => {
    const id = obj.id
    const uri = obj.uri

    const response = await fetch(uri);
    const blob = await response.blob();
    var ref = storageUpload(storage, path)
    await uploadBytes(ref, blob, metadata)

    await getDownloadURL(ref)
      .then((metadata) => {
        let array = imagesUriArray // <-- reference to state
                    
        let object = {
          "id": id,
          "uri": metadata
        }

        array.push(object)         // <-- state mutation
        setImageUriArray(array)    // <-- same state reference
      })
      .catch((error) => {
        console.log(error)
      });
  })
};

解决方案

使用功能状态更新从以前的状态更新。创建先前状态的浅表副本并附加新数据。

const uploadImages = () => {
  ...
  
  pictures.forEach( async (obj) => {
    const { id, uri } = obj;

    const response = await fetch(uri);
    const blob = await response.blob();
    const ref = storageUpload(storage, path);
    await uploadBytes(ref, blob, metadata);

    await getDownloadURL(ref)
      .then((uri) => {
        setImageUriArray(imagesUriArray => [
          ... imagesUriArray, // <-- shallow copy
          { id, uri },        // <-- append new object
        ]);
      })
      .catch(console.log);
  })
};

更新

uploadImages 需要 return 一个 Promise,以便 addItem 可以等待它完成其异步代码。 addItem 还需要访问 uploadImages 更新的 imagesUriArray

pictures 数组映射到最终 return s 具有 id 和新的 uri 属性的对象。

const uploadImages = () => {
  ...

  const fetchImageUri = async ({ id, uri }) => {
    try {
      const response = await fetch(uri);
      const blob = await response.blob();
      const ref = storageUpload(storage, path);
      await uploadBytes(ref, blob, metadata);

      const newUri = await getDownloadURL(ref);
      return { id, uri: newUrl };
    } catch(error) {
      console.log(error);
    }
  }

  return Promise.all(pictures.map(fetchImageUri));
};

更新 addItem 以等待包含已上传图像数据的已解析 Promise 数组。将 imagesUriArray 状态更新排入队列,然后继续引用 uploadImages.

中 returned uploadedImages 数组的其余函数
const addItem = async () => {
  const uploadedImages = await uploadImages();
  setImageUriArray(imagesUriArray => [
    ...imagesUriArray, // <-- shallow copy
    ...uploadedImages, // <-- append new objects
  ]);

  const changes = ref(db, path);
  get(changes).then(async (snapshot) => {
    if (snapshot.val().data !== undefined) {
      const fetchedArray = snapshot.val().data;
      const object = {
        id: `${Math.random()}`,
        images: uploadedImages,
      };

      update(changes, { data: [...fetchedArray, object] });     
    }
  });
}