React Redux多城市商品列表渲染问题求助
解决React Redux单页面渲染多城市商品列表的问题
嘿,这个问题我之前也碰到过!核心问题就是你的状态结构没法区分不同城市的数据,咱们来一步步调整,让你的store能同时存储多组城市的商品信息:
1. 重构State结构
首先把原来的items数组改成以城市名为键的对象,这样就能把每个城市的商品列表对应存储。另外建议加上按城市区分的加载状态和错误信息,让UI能更好地反馈请求状态:
import { fromJS } from 'immutable'; const initialState = fromJS({ items: {}, // 键:城市名;值:对应城市的商品数组 loading: {}, // 键:城市名;值:该城市请求是否在加载中 errors: {} // 键:城市名;值:该城市请求的错误信息(如果有) });
如果不用Immutable.js,普通对象的初始状态可以写成这样:
const initialState = { items: {}, loading: {}, errors: {} };
2. 修改Reducer逻辑
接下来要让reducer根据action里的city参数,精准更新对应城市的状态。这里假设你用了redux-saga或者thunk,action会携带city和返回的items数据:
用Immutable.js的Reducer示例
export default function reducer(state = initialState, action) { switch (action.type) { case fetchItemsByCityRoutine.REQUEST: // 标记该城市请求开始加载,清空之前的错误 return state .setIn(['loading', action.payload.city], true) .setIn(['errors', action.payload.city], null); case fetchItemsByCityRoutine.SUCCESS: // 存储该城市的商品数据,标记加载完成 return state .setIn(['items', action.payload.city], fromJS(action.payload.items)) .setIn(['loading', action.payload.city], false); case fetchItemsByCityRoutine.FAILURE: // 存储错误信息,标记加载完成 return state .setIn(['errors', action.payload.city], action.payload.error) .setIn(['loading', action.payload.city], false); default: return state; } }
不用Immutable.js的Reducer示例(注意不可变更新)
export default function reducer(state = initialState, action) { switch (action.type) { case fetchItemsByCityRoutine.REQUEST: return { ...state, loading: { ...state.loading, [action.payload.city]: true }, errors: { ...state.errors, [action.payload.city]: null } }; case fetchItemsByCityRoutine.SUCCESS: return { ...state, items: { ...state.items, [action.payload.city]: action.payload.items }, loading: { ...state.loading, [action.payload.city]: false } }; case fetchItemsByCityRoutine.FAILURE: return { ...state, errors: { ...state.errors, [action.payload.city]: action.payload.error }, loading: { ...state.loading, [action.payload.city]: false } }; default: return state; } }
3. 确保Action传递City参数
你的action创建函数需要接收city作为参数,发起请求时带上这个参数,并且在成功/失败回调里把city传回reducer:
// 以redux-thunk为例的action示例 export const fetchItemsByCity = (city) => async (dispatch) => { try { // 发起请求前dispatch REQUEST action,携带city dispatch(fetchItemsByCityRoutine.request({ city })); const response = await fetch(`/items?city=${encodeURIComponent(city)}`); if (!response.ok) throw new Error('Request failed'); const items = await response.json(); // 成功后dispatch SUCCESS action,携带city和items dispatch(fetchItemsByCityRoutine.success({ city, items })); } catch (error) { // 失败后dispatch FAILURE action,携带city和错误信息 dispatch(fetchItemsByCityRoutine.failure({ city, error: error.message })); } };
4. 组件中使用多城市数据
在首页组件里,你可以遍历需要展示的城市列表,分别触发请求,并根据城市名获取对应的数据:
import { useDispatch, useSelector } from 'react-redux'; import { useEffect } from 'react'; import { fetchItemsByCity } from './actions'; const HomePage = () => { const dispatch = useDispatch(); // 要展示的5个城市 const targetCities = ['Berlin', 'Paris', 'London', 'Madrid', 'Rome']; // 组件挂载时触发所有城市的请求 useEffect(() => { targetCities.forEach(city => { dispatch(fetchItemsByCity(city)); }); }, [dispatch, targetCities]); // 封装一个获取单个城市数据的方法 const getCityItemData = (city) => { return { items: useSelector(state => state.items[city] || []), isLoading: useSelector(state => state.loading[city] || false), error: useSelector(state => state.errors[city] || null) }; }; return ( <div className="home-page"> {targetCities.map(city => { const { items, isLoading, error } = getCityItemData(city); if (isLoading) return <div key={city} className="loading">加载{city}商品中...</div>; if (error) return <div key={city} className="error">加载{city}商品失败:{error}</div>; return ( <section key={city} className="city-items"> <h2>{city} 热门商品</h2> <ul> {items.map(item => ( <li key={item.id} className="item-card"> <h3>{item.name}</h3> <p>价格:{item.price}</p> </li> ))} </ul> </section> ); })} </div> ); }; export default HomePage;
额外小建议
- 避免重复请求:可以在action里先检查store中是否已有该城市的数据,如果有就不再发起请求
- 性能优化:如果城市数量较多,可以考虑用
useMemo缓存获取到的城市数据,避免不必要的重渲染 - Immutable注意事项:如果用了Immutable.js,组件中获取数据时要记得用
getIn方法,比如state.items.getIn([city], fromJS([]))
内容的提问来源于stack exchange,提问作者lomboboo
相关产品推荐
相关产品推荐

