Taisei-rgb
@Taisei-rgb (Taisei Katagiri)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

FirebaseのStorage上では画像が反映されるが、それがCloud Firestore上に反映されず画像が表示できない

解決したいこと

React, Reduxを使い、ECアプリを作成しています。その際にFirebaseのStorage上では画像が保存されるのですが、それがCloud Firestore上だと反映されずビューに画像が表示できない現象を改善したいです。

  • ビューの状態
    本来であれば、白いシャツの画像が表示されるはずです。
    NO IMAGEは画像がない場合に表示するものとしております。
    スクリーンショット 2020-10-14 午前8.50.40.png

  • Firebase上

スクリーンショット 2020-10-14 午前8.51.03.png
スクリーンショット 2020-10-14 午前8.51.28.png

該当するソースコード

ImageArea.jsx
import React, { useCallback } from "react"
import IconButton from "@material-ui/core/IconButton"
import AddPhotoAlternateIcon from '@material-ui/icons/AddPhotoAlternate';
import { makeStyles } from "@material-ui/styles";
import { storage } from "../../firebase/index"
import ImagePreview from "./ImagePreview"

const useStyles = makeStyles({
  icon: {
    height: 48,
    width: 48
  }
})

const ImageArea = (props) => {
  const classes = useStyles();

  const deleteImage = useCallback(async (id) => {
    const ret = window.confirm('この画像を削除しますか?');
    if (!ret) {
      return false
    } else {
      const newImages = props.images.filter(image => image.id !== id);
      props.setImages(newImages);
      return storage.ref('images').child(id).delete()
    }
  }, [props.images])

  const uploadImage = useCallback((event) => {
    const file = event.target.files;
    let blob = new Blob(file, { type: "image/jpeg" });

    // Generate random 16 digits strings
    const S = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    const N = 16;
    const fileName = Array.from(crypto.getRandomValues(new Uint32Array(N))).map((n) => S[n % S.length]).join('')

    const uploadRef = storage.ref('images').child(fileName);
    const uploadTask = uploadRef.put(blob);

    uploadTask.then(() => {
      // Handle successful uploads on complete
      uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
        const newImage = { id: fileName, path: downloadURL };
        props.setImages((prevState => [...prevState, newImage]))
      });
    });
  }, [props.setImages]);


  return (
    <div>
      <div className="p-grid__list-images">
        {props.images.length > 0 && (
          props.images.map(image => <ImagePreview delete={deleteImage} id={image.id} path={image.path} key={image.id} />)
        )}
      </div>
      <div className="u-text-right">
        <span>商品画像を登録する</span>
        <IconButton className={classes.icon}>
          <label>
            <AddPhotoAlternateIcon />
            <input className="u-display-none" type="file" id="image"
              onChange={(event) => uploadImage(event)}
            />
          </label>
        </IconButton>
      </div>
    </div>
  )
}

export default ImageArea
operations.js
import { db, FirebaseTimestamp } from "../../firebase"
import { push } from "connected-react-router"
import { fetchProductsAction } from "./actions"

const productsRef = db.collection("products");

export const fetchProducts = () => {
  return async (dispatch) => {
    productsRef.orderBy("updated_at", "desc").get()
      .then(snapshots => {
        const productList = []
        snapshots.forEach(snapshot => {
          const product = snapshot.data();
          productList.push(product)
        })
        dispatch(fetchProductsAction(productList))
      })
  }
}

export const saveProduct = (id, name, description, category, gender, price, images, sizes) => {
  return async (dispatch) => {
    const timestamp = FirebaseTimestamp.now();

    const data = {
      category: category,
      description: description,
      gender: gender,
      images: images,
      name: name,
      price: parseInt(price, 10),
      sizes: sizes,
      updated_at: timestamp
    }

    if (id === "") {
      const ref = productsRef.doc();
      data.created_at = timestamp;
      id = ref.id;
      data.id = id;
    }

    return productsRef.doc(id).set(data, { merge: true })
      .then(() => {
        dispatch(push("/"))
      }).catch((error) => {
        throw new Error(error)
      })
  }
}
ProductEdit.jsx
import React, { useCallback, useEffect, useState } from "react";
import { db } from "../firebase/index";
import { useDispatch } from "react-redux";
import { PrimaryButton, SelectBox, TextInput } from "../components/UIkit";
import { saveProduct } from "../reducks/products/operations";
import { ImageArea, SetSizesArea } from "../components/Products";

const ProductEdit = () => {
  const dispatch = useDispatch();
  let id = window.location.pathname.split('/product/edit')[1];

  if (id !== "") {
    id = id.split('/')[1];
  }

  const genders = [
    { id: "all", name: "すべて" },
    { id: "male", name: "メンズ" },
    { id: "female", name: "レディース" },
  ];


  const [name, setName] = useState(""),
    [description, setDescription] = useState(""),
    [category, setCategory] = useState(""),
    [gender, setGender] = useState(""),
    [images, setImages] = useState([]),
    [price, setPrice] = useState(""),
    [sizes, setSizes] = useState([]);

  const inputName = useCallback((event) => {
    setName(event.target.value)
  }, [setName])

  const inputDescription = useCallback((event) => {
    setDescription(event.target.value)
  }, [setDescription])

  const inputPrice = useCallback((event) => {
    setPrice(event.target.value)
  }, [setPrice]);

  const categories = [
    { id: "tops", name: "トップス" },
    { id: "shirts", name: "シャツ" },
    { id: "pants", name: "パンツ" },
  ];

  useEffect(() => {
    if (id !== "") {
      db.collection("products").doc(id).get().then(snapshot => {
        const product = snapshot.data();
        setName(product.name);
        setDescription(product.description);
        setImages(product.images);
        setCategory(product.category);
        setGender(product.gender);
        setPrice(product.price);
        setSizes(product.sizes);
      })
    }
  }, [id]);


  return (
    <section>
      <h2 className="u-text__headline u-text-center">
        商品の登録・編集
      </h2>
      <div className="c-section-container">
        <ImageArea images={images} setImages={setImages} />
        <TextInput
          fullWidth={true} label={"商品名"} multiline={false} required={true}
          onChange={inputName} rows={1} value={name} type={"text"}
        />
        <TextInput
          fullWidth={true} label={"商品説明"} multiline={true} required={true}
          onChange={inputDescription} rows={5} value={description} type={"text"}
        />
        <SelectBox
          label={"カテゴリー"} required={true} options={categories} select={setCategory} value={category}
        />
        <SelectBox
          label={"性別"} required={true} options={genders} select={setGender} value={gender}
        />
        <TextInput
          fullWidth={true} label={"価格"} multiline={false} required={true}
          onChange={inputPrice} rows={1} value={price} type={"number"}
        />
        <div className="module-spacer--small" />
        <SetSizesArea sizes={sizes} setSizes={setSizes} />
        <div className="module-spacer--small" />
        <div className="center">
          <PrimaryButton
            label={"商品情報を保存"}
            onClick={() => dispatch(saveProduct(id, name, description, category, gender, price, sizes, images))}
          />
        </div>
      </div>
    </section>
  )
}

export default ProductEdit

自分で試したこと

imagesの情報がきちんとFirebase上にデータとして反映されているかという記述漏れがないかは確認しました。また、URLを取得するためのメソッドgetDownloadURL()もImageArea.jsx内に入っております。数日自分で考えたのですが答えがでず、解決法を教えていただけると嬉しいです。何とぞ、よろしくお願い致します。

0

No Answers yet.

Your answer might help someone💌