React中如何通过复用单个组件处理多独立输入框并计算总价
问题根因
当前所有酒店条目共用了同一个全局状态:HotelList 中存储的是单个数字格式的 nights 状态,所有渲染出的 Hotel 组件都绑定这同一个数值、共用同一个修改方法,点击任意+按钮时修改的都是这个全局唯一的状态值,自然会出现所有输入框数值同步变化的问题。
实现方案
核心逻辑是将「每个酒店对应的入住晚数」与酒店唯一ID绑定,用键值对结构做独立状态管理,不再使用通用的全局数字状态。
具体实现步骤
- 替换原有的单个
nights数字状态,改为以酒店ID为键的对象结构,每个键值对存储对应酒店选择的独立晚数 - 重构修改晚数的方法:给每个方法传入当前操作的酒店ID,只修改对应ID下的晚数数值,顺便补充减按钮、手动输入数值的逻辑,且限制晚数不能小于0
- 关键注意点:每个传递给Hotel组件的修改方法,都要提前绑定当前条目的酒店ID,这样触发修改时才能精准定位要更新的状态,不会出现串值
- 给每个
Hotel组件传递对应ID下的独立晚数,以及绑定了对应ID的修改方法,让每个组件操作自己对应的状态 - 总价计算时遍历所有酒店条目,逐行计算「每晚单价 * 对应晚数」后累加,直接渲染在页面上即可
修改后的完整代码
HotelList 组件
import React, { useState, useEffect } from 'react'; import './HotelList.scss'; import axios from 'axios'; import Hotel from '../Hotel/Hotel'; const HotelList = () => { const [hotel, setHotel] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); // 改为键值对结构的晚数状态:key是酒店ID,value是对应晚数 const [nightsMap, setNightsMap] = useState({}); useEffect(() => { const fetchData = async () => { try { const response = await axios.get('https://60007292cb21e10017af8f04.mockapi.io/api/v1/hotels') setHotel(response.data); setLoading(false); } catch (err) { setLoading(false); setError(err.message); } } fetchData(); }, []) // 给指定ID的酒店晚数+1 const incrementNights = (hotelId) => { setNightsMap(prev => ({ ...prev, [hotelId]: (prev[hotelId] || 0) + 1 })) } // 给指定ID的酒店晚数-1,且不小于0 const decrementNights = (hotelId) => { setNightsMap(prev => ({ ...prev, [hotelId]: Math.max(0, (prev[hotelId] || 0) - 1) })) } // 手动输入修改指定ID的酒店晚数 const updateNights = (hotelId, value) => { setNightsMap(prev => ({ ...prev, [hotelId]: Math.max(0, Number(value) || 0) })) } // 计算所有酒店的总价 const totalPrice = hotel.reduce((sum, htl) => { const currentNights = nightsMap[htl.id] || 0; return sum + htl.price * currentNights; }, 0) return( <div> {!loading ? ( <div> {hotel.map(htl => ( <div key={htl.id}> <hr/> <Hotel title={htl.hotel} description={htl.description} price={htl.price} image={htl.photoURL} // 传入当前酒店ID对应的独立晚数 nights={nightsMap[htl.id] || 0} // 绑定当前酒店ID的修改方法,避免串值 onIncrement={() => incrementNights(htl.id)} onDecrement={() => decrementNights(htl.id)} onNightsChange={(value) => updateNights(htl.id, value)} /> </div> ))} <p>Total price: ${totalPrice}</p> </div> ) : ( <div className="ring"><div className="lds-dual-ring"></div></div> )} </div> ); }; export default HotelList;
Hotel 组件
import React from 'react'; import './Hotel.scss'; const Hotel = ({ title, description, price, image, nights, onIncrement, onDecrement, onNightsChange }) => { return( <div className="container"> <div className="img"> <img src={image} alt="hotel" /> </div> <div className="info"> <h1>{title}</h1> <p>{description}</p> </div> <div className="vl"></div> <div className="price"> <button onClick={onDecrement}>-</button> <input type="number" min="0" value={nights} onChange={(e) => onNightsChange(e.target.value)} /> <button onClick={onIncrement}>+</button> </div> <div className="inner-container"> <i className="fas fa-trash"></i> <p>${price}</p> </div> </div> ) }; export default Hotel;
效果说明
修改后每个酒店的加减按钮触发时,只会修改nightsMap中对应酒店ID的数值,不会影响其他酒店的晚数选择;总价会随每个酒店的晚数变化实时计算更新,不会出现所有输入框联动的问题。
内容的提问来源于stack exchange,提问作者Przemo
相关产品推荐
相关产品推荐

