问题描述
我对 React/Redux 还很陌生。我有一个标准化的 redux 状态,如下所示:
users: {
user1: {
id: 'user1',name: 'Jim',carts: ['cart1','cart2']
}
},carts: {
cart1: {
id: 'cart1',items: ['item1','item2']
},cart2: {
id: 'cart2',items: ['item3','item4']
}
},items: {
item1: {
id: 'item1',price: 1
},item2: {
id: 'item2',price: 2
},item3: {
id: 'item3',price: 3
},item4: {
id: 'item4',price: 4
}
}
我还创建了以下选择器:
item.selector.js:
export const selectAllItems = state => state.items
export const selectItems = itemIds => createSelector(
[selectAllItems],items => Object.keys(items)
.filter(itemId => itemIds.includes(itemId))
.reduce((arr,itemId) => {
arr.push(items[itemId])
return arr
},[])
)
export const totalItemsCost = itemIds => createSelector(
[selectAllItems],items => Object.keys(items)
.filter(itemId => itemIds.includes(itemId))
.reduce((cost,itemId) => {
cost += blocs[itemId].price
return cost
},0)
)
cart.selector.js:
export const selectAllCarts = state => state.carts
export const selectCart = cartId => createSelector(
[selectAllCarts],carts => carts[cartId]
)
export const selectCarts = cartIds => createSelector(
[selectAllCarts],options => Object.keys(carts)
.filter(key => cartIds.includes(key))
.reduce((arr,key) => {
arr.push(options[key])
return arr
},[])
)
user.selector.js:
export const selectUsers = state => state.users
export const selectUser = userId => createSelector(
[selectUsers],users => users[userId]
)
我希望在用户页面上显示每个购物车的总成本并具有以下 React 组件:
class User extends React.Component {
render () {
return (
{ this.props.carts.map(cart => (<displayComponent id={ cart.id } cost={ $TOTAL_CART_COST }/>))}
)
}
}
const mapStatetoProps = (state,ownProps) => ({
user: selectUser(ownProps.match.params.userId)(state),carts: selectCarts(selectUser(ownProps.match.params.userId)(state).carts)(state)
})
export default connect(mapStatetoProps,null)(User)
我不确定实现这一目标的最佳方式。我尝试创建一个由减速器更新的 cartCost 字段,如下所示:
case CartActionTypes.ADD_ITEM_SUCCESS:
return {
...state,[action.payload.cartId]: {
...state[action.payload.cartId],cartCost: totalItemsCost(addItemToCart(state[action.payload.cartId].items,action.payload.itemId))(state),items: addItemToCart(state[action.payload.cartId].items,action.payload.itemId)
}
}
使用:
export const addItemToCart = (items,newItem) => {
const existingItem = items.find(item => item === newItem)
if (existingItem) {
return items
} else {
return [...items,newItem]
}
}
但这给了我:
Uncaught (in promise) TypeError: Cannot convert undefined or null to object
at Function.keys (<anonymous>)
at item.selectors.js:18
at index.js:70
at index.js:30
at index.js:84
at index.js:30
at cartReducer (cart.reducer.js:35)
at combination (redux.js:459)
at persistReducer.js:147
at dispatch (redux.js:213)
at redux-logger.js:1
at index.js:11
at dispatch (redux.js:638)
at item.actions.js:30
欢迎所有建议,也许我没有采取正确的方法在减速器中创建新字段?谢谢
解决方法
这就是我的结局:
cart.selector.js:
export const selectAllCarts = state => state.carts
export const selectCart = cartId => createSelector(
[selectAllCarts,selectAllItems],(carts,items) => generateCart(carts[cartId],items)
)
export const generateCart = (cart,allItems) => {
return {
...cart,cartCost: calculateCartCost(allItems,cart.items)
}
}
export const calculateCartCost = (allItems,cartItemIds) => {
return Object.keys(allItems)
.filter(itemId => cartItemIds.includes(itemId))
.reduce((cost,itemId) => {
cost += allItems[itemId].price
return cost
},0)
}
export const selectCarts = cartIds => createSelector(
[selectAllCarts,items) => Object.keys(carts)
.filter(key => cartIds.includes(key))
.reduce((arr,key) => {
arr.push(generateCart(carts[key],items))
return arr
},[])
)
现在给了我一个购物车列表,所有购物车都有我可以用来显示的购物车成本字段。很确定它不是最佳的,但我拥有的最好的 :)