【问题标题】:database (firestore) call is only taking the first value数据库(firestore)调用只取第一个值
【发布时间】:2021-06-27 22:22:09
【问题描述】:

我想调用数据库(firestore)以实时返回用户已将产品添加到购物车的产品数据,然后该数据将用于代码中以获取购物车的总价格项目所以我尝试了以下方法:

这个useEfect 将进行数据库调用,并将项目状态设置为一个对象数组,其中包含每个购物车项目的实时 ID 和数量强>

const [items, setItems] = useState([]);
const [subTotal, setSubTotal] = useState([]);
const [Total, setTotal] = useState(0);
const oneSubTotal = [];

useEffect(() => {
  db.collection("users").doc("4sfrRMB5ROMxXDvmVdwL").collection("basket").onSnapshot((docs) => {
    let array = []
    docs.forEach(doc =>{
      array.push(doc.data())
      console.log(array)
      setItems(array)
    })
  });
}, [])

此代码应循环遍历 items 数组以在通过对数据库的另一次调用获取每个项目的价格后将元素 item.price 添加到每个对象,然后通过推送将每个项目的总价格推送到 Subtotal 数组数量乘以价格

useEffect(() => {
  items && items.forEach((item) => {
    // console.log(item)
    const id = item.id
    db.collection("products").doc(id).get().then((e)=>{
      item.price = (e.data().price)
      oneSubTotal.push(item.price * item.quantity)
      setSubTotal(oneSubTotal)
    })
})

然后这段代码将循环遍历小计数组以获取项目价格的总和

let sum = 0; 
for (let num of subTotal){
  sum = sum + num
}
useEffect(() => {
  setTotal(sum)
}, [sum, items])

但问题是当页面第一次呈现时Total 的值始终是第一个项目的总价,它由 items 数组中的第一个对象表示,并且当我修改任何项目的数量时 (不刷新页面) Total 值会在几秒钟内显示正确的金额,然后它的值会返回以仅显示第一件商品的总价

【问题讨论】:

    标签: javascript reactjs firebase google-cloud-firestore


    【解决方案1】:

    在您的onSnapshot 处理程序中,您过早地调用console.log(array)setItems(array)。这可能会导致您的应用被多次渲染。您应该确保在 forEach 循环之外调用这些行。

    .onSnapshot((docs) => {
      let array = []
      docs.forEach(doc => {
        array.push(doc.data())
      });
      console.log(array)
      setItems(array)
    });
    

    但即便如此,在调用setItems 之前获取商品价格会更有效。此外,您不应使用forEach 逐个调用数据库,而应将请求捆绑成批处理,如this answer 中所示,它可用作实用函数fetchDocumentsWithId()

    .onSnapshot((docs) => {
      const cartItems = [], itemPriceObject = {};
      // cartItems will be ({ id: string, quantity: number, price: number | null, lineTotal: number })[]
      // itemPriceObject will be a map of IDs to their price (a Record<string, number | null>) (used to deduplicate IDs & store prices)
      
      docs.forEach(doc => {
        const cartItem = doc.data()
        cartItems.push(cartItem)
        itemPriceObject[cartItem.id] = null
      });
      
      // idsArray is a deduplicated list of IDs
      const idsArray = Object.keys(itemPriceObject);
      
      fetchDocumentsWithId(
        db.collection("products"),
        idsArray,
        (itemDoc) => {
          itemPriceObject[itemDoc.id] = itemDoc.get("price") // more efficient than itemDoc.data().price
        }
      )
        .then(() => {
          // if here, all prices (that are available) have been retrieved
          // MAY BE NULL! Consider these items to be "not available"
    
          const totalSum = 0
          
          // put prices in their items, calculate line cost and add to total
          cartItems.forEach(item => {
            item.price = itemPriceObject[item.id]
            item.lineTotal = item.price === null ? 0 : item.price * item.quantity
            totalSum += item.lineTotal
          }
          
          // set items & total sum
          setItems(cartItems)
          setTotal(totalSum)
        })
        .catch((error) => {
          // failed to retrieve some documents from the database
          // TODO: update UI
        });
    });
    

    注意:为了清楚起见,subTotal(意思是:一些但不是所有值的总和)被重命名为lineTotal(意思是:这个条目/行中项目的成本, cost x quantity)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-08
      • 1970-01-01
      • 2022-03-15
      • 1970-01-01
      相关资源
      最近更新 更多