You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:10:27