当我需要一个页面呈现来自同一API的多个项目列表时,我遇到了React Redux的问题。例如,我有GET参数/items
的端点/items?city=Berlin
。我需要在首页上呈现5个城市的项目列表。
目前我有1个动作来处理API调用。
这是我的 reducer.js :
...
const initialState = fromJS({
...
items: [],
...
})
export default function reducer (state = initialState, action) {
...
switch (action.type) {
case fetchItemsByCityRoutine.SUCCESS:
return state.update('items', (items) => items.push(...action.payload.data))
...
} }
saga.js :
function * fetchItemsByCity ({ payload: city }) {
try {
yield put(fetchItemsByCityRoutine.request())
const response = yield call(apiClient.fetchItemsByCity, city)
response.city = city
yield put(fetchItemsByCityRoutine.success(response))
} catch (error) {
yield put(fetchItemsByCityRoutine.failure(extractErrorMessage(error)))
} finally {
yield put(fetchItemsByCityRoutine.fulfill())
}
}
function * watchItemsByCitySaga () {
yield takeEvery(fetchItemsByCityRoutine.TRIGGER, fetchItemsByCity)
}
在主页上,我会像这样呈现城市列表:
const renderCityListSections = () => homepageCityLists.map((city) => <ItemList key={city.cityName} {...city} />)
ItemList组件:
class ItemList extends Component {
componentDidMount () {
const { cityName } = this.props
this.props.fetchItemsByCityRoutine(cityName)
}
render () {
const { items, title } = this.props
return (
items.length > 0 &&
<Wrapper>
<SlickSlider>
{items.map((item) => <Item key={item.id} {...item} />)}
</SlickSlider>
</Wrapper>
)
}
}
问题是当前解决方案使重新渲染视图的次数太多,因为每次我为某个城市获取新列表时它会添加到items
,因此项目会更改并导致触发重新渲染视图。
我想过要为每个城市创建5种不同的动作,但这似乎不是合理的解决方案。
在一个页面上呈现多个城市列表的最佳方法是什么?
更新:
所以我将 reducer 更改为:
const initialState = fromJS({
...
items: {
Miami: [],
Prague: [],
Melbourne: [],
Venice: [],
Barcelona: [],
},
...
})
export default function reducer (state = initialState, action) {
switch (action.type) {
...
case fetchItemsByCityRoutine.SUCCESS:
return state.updateIn(['items', action.payload.city], (list) => (
list.concat(action.payload.data)
))
...
}
}
遇到这种问题的每个人我最终都得到了这个解决方案:
在我的 reducer 中,我将城市的每个项目分开,如下所示:
const initialState = fromJS({
...
itemsForBerlin: [],
itemsForAnotherCity: [],
...
})
export default function reducer (state = initialState, action) {
switch (action.type) {
...
case fetchItemsByCityRoutine.SUCCESS:
return state.set(`itemsFor${action.payload.city}`, action.payload.data)
...
}
}