如何在 redux 中不重复地更新数量?
How do I update the quantity without duplicate in redux?
我有添加和删除项目状态,它发送到购物车。我是我的购物车,当我添加时,它会重复。我是否需要为此增量创建一个减速器,以便它可以从 1 更新到 2 而不是复制它?
const INITIAL_STATE = []
export const addItem = createAction('ADD_ITEM')
export const increment = createAction('INCREMENT')
export const removeItem = createAction('REMOVE_ITEM')
export default createReducer(INITIAL_STATE, {
[addItem.type]: (state, action) => [...state, action.payload],
[removeItem.type]: (state, action) => state.filter(item => item._id !== action.payload._id)
})
这是我的产品:
const INITIAL_STATE = []
export const addProducts = createAction('ADD_PRODUCTS')
export const addProduct = createAction('ADD_PRODUCT')
export default createReducer(INITIAL_STATE, {
[addProducts.type]: (state , action) => [...state, action.payload],
[addProduct.type]: (state, action) => [...action.payload]
})
我的减速器:
export default configureStore({
reducer: {
products: productsReducer,
cart: cartReducer
}
})
您的减速器有必要验证项目 ID 是否存在。案例存在,更新项目计数。而不是创建一个计数等于 1 的新项目:
export default createReducer(INITIAL_STATE, {
[addItem.type]: (state, action) => {
const pos = state.map((i) => i._id).indexOf(action.payload._id);
if (pos !== -1) {
state[pos] = { ...action.payload, count: state[pos].count + 1 };
} else {
state.push({ ...action.payload, count: 1 });
}
},
[removeItem.type]: (state, action) =>
state.filter((item) => item._id !== action.payload._id),
});
数量
现在您的 cart
状态正在保存购物车中所有商品的数组。任何地方都没有quantity
属性。您可以做的一件事是在将项目添加到购物车时向项目对象添加 quantity: 1
属性。但是...
正常化状态
您实际上不需要保存 while item
对象,因为您已经在 products
中拥有该信息。您真正需要知道的是购物车中每件商品的 id
及其 quantity
。最合乎逻辑的数据结构是字典对象,其中键是项目 ID,值是相应的数量。
因为你只需要 id
我建议你的动作创建者 addItem
、removeItem
和 increment
应该只是 [=17= 的函数] 而不是整个 item
。这意味着您可以像 dispatch(addItem(5))
这样称呼它们,而您的 action.payload
就是 id
。 addProduct
和 addProducts
操作仍然需要整个 item
对象。
一组产品就可以了,您可以随时调用 state.products.find(item => item._id === someId)
以通过其 ID 查找产品。但是带键的对象更好!您希望键是项目 ID,值是相应的对象。 Redux Toolkit 通过 createEntityAdapter
帮助程序内置了对这种结构的支持。
创建切片
尽管 createAction
定义您的操作没有任何问题,但不需要。您可以将 createAction
和 createReducer
替换为结合了两者功能的 createSlice
。
这是编写产品的另一种方式:
import { createSlice, createEntityAdapter } from "@reduxjs/toolkit";
const productsAdapter = createEntityAdapter({
selectId: (item) => item._id
});
const productsSlice = createSlice({
// this becomes the prefix for the action names
name: "products",
// use the initial state from the adapter
initialState: productsAdapter.getInitialState(),
// your case reducers will create actions automatically
reducers: {
// you can just pass functions from the adapter
addProduct: productsAdapter.addOne,
addProducts: productsAdapter.addMany
// you can also add delete and update actions easily
}
});
export default productsSlice.reducer;
export const { addProduct, addProducts } = productsSlice.actions;
// the adapter also creates selectors
const productSelectors = productsAdapter.getSelectors(
// you need to provide the location of the products relative to the root state
(state) => state.products
);
// you can export the selectors individually
export const {
selectById: selectProductById,
selectAll: selectAllProducts
} = productSelectors;
你的购物车:
import {createSlice, createSelector} from "@reduxjs/toolkit";
const cartSlice = createSlice({
name: 'cart',
// an empty dictionary object
initialState: {},
reducers: {
addItem: (state, action) => {
const id = action.payload;
// add to the state with a quantity of 1
state[id] = 1;
// you might want to see if if already exists before adding
},
removeItem: (state, action) => {
const id = action.payload;
// you can just use the delete keyword to remove it from the draft
delete state[id];
},
increment: (state, action) => {
const id = action.payload;
// if you KNOW that the item is already in the state then you can do this
state[id]++;
// but it's safer to do this
// state[id] = (state[id] || 0) + 1
}
}
})
export default cartSlice.reducer;
export const {addItem, removeItem, increment} = cartSlice.actions;
// you can select the data in any format
export const selectCartItems = createSelector(
// only re-calculate when this value changes
state => state.cart,
// reformat into an an array of objects with propeties id and quantity
(cart) => Object.entries(cart).map(([id, quantity]) => ({id, quantity}))
)
// select the quantity for a particular item by id
export const selectQuantityById = (state, id) => state.cart[id]
// you can combine the ids with the products, but
// I actually recommend that you just return the ids and get the
// product data from a Product component like <Product id={5} quantity={2}/>
export const selectCartProducts = createSelector(
// has two input selectors
state => state.cart,
state => state.products,
// combine and reformat into an array of objects
(cart, products) => Object.keys(cart).map(id => ({
// all properties of the product
...products.entries[id],
// added quantity property
quantity: cart[id],
}))
)
我有添加和删除项目状态,它发送到购物车。我是我的购物车,当我添加时,它会重复。我是否需要为此增量创建一个减速器,以便它可以从 1 更新到 2 而不是复制它?
const INITIAL_STATE = []
export const addItem = createAction('ADD_ITEM')
export const increment = createAction('INCREMENT')
export const removeItem = createAction('REMOVE_ITEM')
export default createReducer(INITIAL_STATE, {
[addItem.type]: (state, action) => [...state, action.payload],
[removeItem.type]: (state, action) => state.filter(item => item._id !== action.payload._id)
})
这是我的产品:
const INITIAL_STATE = []
export const addProducts = createAction('ADD_PRODUCTS')
export const addProduct = createAction('ADD_PRODUCT')
export default createReducer(INITIAL_STATE, {
[addProducts.type]: (state , action) => [...state, action.payload],
[addProduct.type]: (state, action) => [...action.payload]
})
我的减速器:
export default configureStore({
reducer: {
products: productsReducer,
cart: cartReducer
}
})
您的减速器有必要验证项目 ID 是否存在。案例存在,更新项目计数。而不是创建一个计数等于 1 的新项目:
export default createReducer(INITIAL_STATE, {
[addItem.type]: (state, action) => {
const pos = state.map((i) => i._id).indexOf(action.payload._id);
if (pos !== -1) {
state[pos] = { ...action.payload, count: state[pos].count + 1 };
} else {
state.push({ ...action.payload, count: 1 });
}
},
[removeItem.type]: (state, action) =>
state.filter((item) => item._id !== action.payload._id),
});
数量
现在您的 cart
状态正在保存购物车中所有商品的数组。任何地方都没有quantity
属性。您可以做的一件事是在将项目添加到购物车时向项目对象添加 quantity: 1
属性。但是...
正常化状态
您实际上不需要保存 while item
对象,因为您已经在 products
中拥有该信息。您真正需要知道的是购物车中每件商品的 id
及其 quantity
。最合乎逻辑的数据结构是字典对象,其中键是项目 ID,值是相应的数量。
因为你只需要 id
我建议你的动作创建者 addItem
、removeItem
和 increment
应该只是 [=17= 的函数] 而不是整个 item
。这意味着您可以像 dispatch(addItem(5))
这样称呼它们,而您的 action.payload
就是 id
。 addProduct
和 addProducts
操作仍然需要整个 item
对象。
一组产品就可以了,您可以随时调用 state.products.find(item => item._id === someId)
以通过其 ID 查找产品。但是带键的对象更好!您希望键是项目 ID,值是相应的对象。 Redux Toolkit 通过 createEntityAdapter
帮助程序内置了对这种结构的支持。
创建切片
尽管 createAction
定义您的操作没有任何问题,但不需要。您可以将 createAction
和 createReducer
替换为结合了两者功能的 createSlice
。
这是编写产品的另一种方式:
import { createSlice, createEntityAdapter } from "@reduxjs/toolkit";
const productsAdapter = createEntityAdapter({
selectId: (item) => item._id
});
const productsSlice = createSlice({
// this becomes the prefix for the action names
name: "products",
// use the initial state from the adapter
initialState: productsAdapter.getInitialState(),
// your case reducers will create actions automatically
reducers: {
// you can just pass functions from the adapter
addProduct: productsAdapter.addOne,
addProducts: productsAdapter.addMany
// you can also add delete and update actions easily
}
});
export default productsSlice.reducer;
export const { addProduct, addProducts } = productsSlice.actions;
// the adapter also creates selectors
const productSelectors = productsAdapter.getSelectors(
// you need to provide the location of the products relative to the root state
(state) => state.products
);
// you can export the selectors individually
export const {
selectById: selectProductById,
selectAll: selectAllProducts
} = productSelectors;
你的购物车:
import {createSlice, createSelector} from "@reduxjs/toolkit";
const cartSlice = createSlice({
name: 'cart',
// an empty dictionary object
initialState: {},
reducers: {
addItem: (state, action) => {
const id = action.payload;
// add to the state with a quantity of 1
state[id] = 1;
// you might want to see if if already exists before adding
},
removeItem: (state, action) => {
const id = action.payload;
// you can just use the delete keyword to remove it from the draft
delete state[id];
},
increment: (state, action) => {
const id = action.payload;
// if you KNOW that the item is already in the state then you can do this
state[id]++;
// but it's safer to do this
// state[id] = (state[id] || 0) + 1
}
}
})
export default cartSlice.reducer;
export const {addItem, removeItem, increment} = cartSlice.actions;
// you can select the data in any format
export const selectCartItems = createSelector(
// only re-calculate when this value changes
state => state.cart,
// reformat into an an array of objects with propeties id and quantity
(cart) => Object.entries(cart).map(([id, quantity]) => ({id, quantity}))
)
// select the quantity for a particular item by id
export const selectQuantityById = (state, id) => state.cart[id]
// you can combine the ids with the products, but
// I actually recommend that you just return the ids and get the
// product data from a Product component like <Product id={5} quantity={2}/>
export const selectCartProducts = createSelector(
// has two input selectors
state => state.cart,
state => state.products,
// combine and reformat into an array of objects
(cart, products) => Object.keys(cart).map(id => ({
// all properties of the product
...products.entries[id],
// added quantity property
quantity: cart[id],
}))
)