React遍历渲染产品时,点击如何仅展示对应产品的详情?
问题根源
当前全局Context里的detailsForProduct是单一变量,所有Product组件共用这个值。点击任意产品后,全局状态更新,所有组件都会读取同一个详情数据,导致全部产品显示相同内容。
解决方案
核心思路是让全局状态按产品维度存储详情和加载状态,每个Product组件只读取自身对应产品的数据。
1. 更新Context的状态结构
修改Context的初始状态,用对象分别存储每个产品的详情和加载状态:
// 假设这是你的SearchContext初始状态 const initialState = { detailsMap: {}, // key: 产品唯一标识(name或_id), value: 对应详情 fetchingMap: {} // key: 产品唯一标识, value: 是否正在加载 };
2. 修改fetchDetailsForProduct方法
更新Context里的请求逻辑,针对单个产品维护状态:
// 示例reducer逻辑(根据你的实际Context实现调整) const reducer = (state, action) => { switch (action.type) { case 'FETCH_DETAILS_START': return { ...state, fetchingMap: { ...state.fetchingMap, [action.payload.productId]: true } }; case 'FETCH_DETAILS_SUCCESS': return { ...state, fetchingMap: { ...state.fetchingMap, [action.payload.productId]: false }, detailsMap: { ...state.detailsMap, [action.payload.productId]: action.payload.details } }; case 'FETCH_DETAILS_ERROR': return { ...state, fetchingMap: { ...state.fetchingMap, [action.payload.productId]: false } }; default: return state; } }; // 对应的fetchDetailsForProduct方法 const fetchDetailsForProduct = async (product) => { const productId = product._id; // 用_id作为唯一标识更可靠 dispatch({ type: 'FETCH_DETAILS_START', payload: { productId } }); try { const response = await apiCallToGetDetails(product); // 替换成你的API请求 dispatch({ type: 'FETCH_DETAILS_SUCCESS', payload: { productId, details: response.data } }); } catch (error) { dispatch({ type: 'FETCH_DETAILS_ERROR', payload: { productId } }); } };
3. 修改App组件
传递当前产品对应的详情和加载状态给Product组件:
const App = () => { const { state: { detailsMap, fetchingMap }, fetchDetailsForProduct } = useContext(SearchContext); const getDetails = async (product) => { await fetchDetailsForProduct(product); }; return ( <FlatList data={data} renderItem={({ item }) => { const productId = item._id; return ( <Product product={item} getDetails={getDetails} details={detailsMap[productId]} isFetching={fetchingMap[productId] || false} /> ); }} keyExtractor={item => item._id} /> ); };
4. 调整Product组件逻辑
确保只渲染当前产品的详情,同时优化请求逻辑(已加载过则不再重复请求):
const Product = ({ getDetails, product, details, isFetching }) => { const [expanded, setExpanded] = useState(false); const productId = product._id; const handlePress = async () => { const newExpanded = !expanded; setExpanded(newExpanded); // 展开且未加载过详情时才发起请求 if (newExpanded && !details) { await getDetails(product); } }; return ( <> <ListItem.Accordion content={ <> <ListItem> <View> <Text>{product.name}</Text> </View> </ListItem> </> } isExpanded={expanded} onPress={handlePress} > <Details isFetching={isFetching} details={details} /> </ListItem.Accordion> </> ); };
额外优化建议
- 始终用产品的
_id作为唯一标识,避免同名产品导致的数据混乱 - 可在Context中添加缓存过期逻辑,防止长期存储无效数据
- 补充错误状态处理,给用户展示加载失败的提示
内容的提问来源于stack exchange,提问作者user6377312
相关产品推荐
相关产品推荐

