React JS:如何为map循环中未初始化的数组添加Key并实现数量增减功能?
解决React + Firebase购物车数量管理的几个问题
嘿,我来帮你一步步搞定这些麻烦!你遇到的都是React状态管理和列表渲染里的典型问题,咱们逐个击破:
1. 数组初始化异常的问题
你一开始用new Array(libros.length).fill(1)初始化cantidad,但组件首次渲染时libros还是空数组(因为Firebase数据是异步获取的),所以这个数组的长度是0,自然达不到预期效果。
解决方案:在获取到Firebase数据后再初始化cantidad数组。修改你的useEffect,在设置libros的同时更新cantidad:
useEffect(() => { queryRef.orderBy("precio") .get() .then((snapshot) => { const tempData = []; snapshot.forEach((doc) => { const data = doc.data(); // 顺便把文档ID存下来,后面用来当key tempData.push({...data, id: doc.id}); }); setLibros(tempData); // 获取到数据后初始化数量数组 setCantidad(new Array(tempData.length).fill(1)); }); }, []);
2. Key警告的问题
React要求列表渲染的每个元素都要有唯一的key属性,用来识别元素变化。你用index当key不是不行,但如果列表元素有增删,容易出问题。最好用Firebase文档的唯一ID当key,这也是我们刚才在tempData里存id的原因。
修改map渲染的<tr>:
{libros.map((libro, index) => ( <tr key={libro.id}> {/* 这里加上唯一key */} {/* ...其他内容 */} </tr> ))}
3. 状态更新不生效的问题
React的状态是不可变的,你直接修改数组元素cantidad[index] + 1然后传给setCantidad是错误的——React检测不到数组的变化,因为你没有创建新的数组引用。
正确的做法是先复制原数组,修改后再更新状态:
修复mas函数
const mas = (index) => { // 复制原数组,创建新引用 const newCantidad = [...cantidad]; newCantidad[index] += 1; setCantidad(newCantidad); };
修复menos函数
你的原函数里还有逻辑错误:当数量已经是0时,不应该把整个cantidad设为0,而是只保持当前index的值为0。修改后:
const menos = (index) => { if (cantidad[index] <= 0) { window.alert("Sorry, Zero limit reached"); return; } const newCantidad = [...cantidad]; newCantidad[index] -= 1; setCantidad(newCantidad); };
4. 额外的小优化
- 你的按钮是空的,用户看不到,给按钮加文字:
<button onClick={() => mas(index)}>+</button> {cantidad[index] || 0} {/* 加个默认值,避免初始时undefined */} <button onClick={() => menos(index)}>-</button> - 计算总价:可以用
useEffect监听libros和cantidad的变化,自动计算总价:
然后在页面上展示:useEffect(() => { const total = libros.reduce((sum, libro, index) => { return sum + parseFloat(libro.precio) * (cantidad[index] || 0); }, 0); setTotalPrice(total.toFixed(2)); }, [libros, cantidad]);<h2 className="total-price">Total: ${totalPrice || 0}</h2>
修改后的完整代码
import React, { useState, useEffect } from 'react' import { auth, db } from './firebase'; import { useHistory } from 'react-router-dom'; import { Checkbox } from '@material-ui/core'; function CrearPedidos({user}) { const [libros, setLibros] = useState([]); const [cantidad, setCantidad] = useState([]); // 初始化为空数组 const history = useHistory(); const [totalPrice, setTotalPrice] = useState(0); const librosRef = db.collection('libros'); const queryRef = librosRef.where('grado', '==', '4° Grado'); useEffect(() => { queryRef.orderBy("precio") .get() .then((snapshot) => { const tempData = []; snapshot.forEach((doc) => { const data = doc.data(); // 保存文档ID用于key tempData.push({...data, id: doc.id}); }); setLibros(tempData); // 初始化数量数组 setCantidad(new Array(tempData.length).fill(1)); }); }, []); // 计算总价 useEffect(() => { const total = libros.reduce((sum, libro, index) => { return sum + parseFloat(libro.precio) * (cantidad[index] || 0); }, 0); setTotalPrice(total.toFixed(2)); }, [libros, cantidad]); const mas = (index) => { const newCantidad = [...cantidad]; newCantidad[index] += 1; setCantidad(newCantidad); }; const menos = (index) => { if (cantidad[index] <= 0) { window.alert("Sorry, Zero limit reached"); return; } const newCantidad = [...cantidad]; newCantidad[index] -= 1; setCantidad(newCantidad); }; // 这里可以实现realizarPedidos函数,比如提交选中的书籍和数量到Firebase const realizarPedidos = () => { // 示例逻辑:收集选中的书籍和对应数量 const pedido = libros.map((libro, index) => ({ ...libro, cantidad: cantidad[index] || 0 })).filter(item => item.cantidad > 0); // 然后提交到Firebase,比如db.collection('pedidos').add({...}) console.log('Pedido a realizar:', pedido); // history.push('/ruta-de-confirmacion'); }; return ( <div className="listado_Pedidos"> <div className="estudiantes_container"> <h1 className = "estudiantes_container_h1">Estudiante: {user?.displayName}</h1> <h1 className = "estudiantes_container_h1">Libros Nuevos</h1> <h2 className="total-price">Total: ${totalPrice}</h2> <div className ="tableContainer"> <table> <thead> <tr className="Lista"> <th>Cantidad</th> <th>Grado</th> <th>Descripcion</th> <th>Editorial</th> <th>Precio</th> </tr> </thead> <tbody> {libros.map((libro, index) => ( <tr key={libro.id}> <td> <button onClick={() => mas(index)}>+</button> {cantidad[index] || 0} <button onClick={() => menos(index)}>-</button> </td> <td>{libro.grado}</td> <td> <input type="checkbox" {/* 如果需要管理选中状态,这里应该用一个状态数组,比如checkedBooks */} /> {libro.descripcion} </td> <td>{libro.editorial}</td> <td>${(parseFloat(libro.precio) * (cantidad[index] || 0)).toFixed(2)}</td> </tr> ))} </tbody> </table> </div> <div className="space" /> <button onClick={realizarPedidos} className = "crear_estudiante_boton">Realizar Pedidos</button> <div className="space" /> </div> </div> ) } export default CrearPedidos
这些修改应该能解决你所有的问题啦!核心就是记住React状态的不可变性,以及异步数据加载时的状态初始化时机。
内容的提问来源于stack exchange,提问作者React Enjoyer
相关产品推荐
相关产品推荐

