Javascript 如何更新数量而不在redux中重复?

Javascript 如何更新数量而不在redux中重复?,javascript,reactjs,redux,redux-toolkit,Javascript,Reactjs,Redux,Redux Toolkit,我有添加和删除项目的状态,它会发送到购物车。我的购物车,当我添加,它重复。我是否需要为此增量创建一个减速机,以便它可以从1更新为2,而不是复制它 const INITIAL_STATE = [] export const addItem = createAction('ADD_ITEM') export const increment = createAction('INCREMENT') export const removeItem = createAction('REMOVE_ITEM

我有添加和删除项目的状态,它会发送到购物车。我的购物车,当我添加,它重复。我是否需要为此增量创建一个减速机,以便它可以从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]
})
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;
我的减速机:

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),
});
量 现在,您的
购物车
状态正在保存购物车中所有项目的数组。任何地方都没有
数量
属性。您可以做的一件事是在将商品添加到购物车时,将
quantity:1
属性添加到item对象。但是

正常化状态 您实际上不需要保存while
对象,因为您在
产品
中已经有了该信息。您真正需要知道的是购物车中每个商品的
id
及其
数量。最符合逻辑的数据结构是dictionary对象,其中键是项ID,值是相应的数量

由于您只需要
id
,我建议您的动作创建者
addItem
removietem
increment
应该只是
id
的一个函数,而不是整个
项。这意味着您将像调用
dispatch(addItem(5))
一样调用它们,而您的
action.payload
将只是
id
addProduct
addProducts
操作仍然需要整个
对象

产品数组很好,您可以随时调用
state.products.find(item=>item.\u id==someId)
以通过其id查找产品。但是设置关键帧的对象更好!您希望键是项ID,值是相应的对象。Redux Toolkit内置了对该结构和帮助程序的支持

创建切片 尽管
createAction
,但定义操作没有任何问题,但它不是必需的。您可以将
createAction
createReducer
替换为结合了两者功能的

以下是编写产品的另一种方式:

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]
})
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],
  }))
)
从“@reduxjs/toolkit”导入{createSlice,createSelector};
常量cartSlice=createSlice({
名称:'购物车',
//空字典对象
初始状态:{},
减速器:{
附加项:(状态、操作)=>{
const id=action.payload;
//添加到数量为1的状态
状态[id]=1;
//在添加之前,您可能需要查看是否已经存在
},
removeItem:(状态、操作)=>{
const id=action.payload;
//您可以使用delete关键字将其从草稿中删除
删除状态[id];
},
增量:(状态、动作)=>{
const id=action.payload;
//如果您知道项目已处于状态,则可以执行此操作
状态[id]++;
//但这样做更安全
//状态[id]=(状态[id]| | 0)+1
}
}
})
导出默认cartSlice.reducer;
导出常量{addItem,removItem,increment}=cartSlice.actions;
//您可以选择任何格式的数据
导出常量selectCartItems=createSelector(
//仅当此值更改时重新计算
state=>state.cart,
//重新格式化为具有属性id和数量的对象数组
(购物车)=>Object.entries(购物车).map([id,quantity])=>({id,quantity}))
)
//按id选择特定项目的数量
export const selectQuantityById=(state,id)=>state.cart[id]
//您可以将ID与产品组合,但是
//实际上,我建议您只需返回ID并获取
//来自产品组件的产品数据,如
导出常量selectCartProducts=createSelector(
//有两个输入选择器
state=>state.cart,
state=>state.products,
//合并并重新格式化为对象数组
(购物车,产品)=>Object.keys(购物车).map(id=>({
//产品的所有属性
…产品。条目[id],
//添加数量属性
数量:购物车[id],
}))
)

他们正在使用Redux Toolkit中的函数,该函数使用可变草稿状态,因此实际上您不需要克隆任何内容。如果您不介意的话,我将编辑您的答案,因为您可以在Redux Toolkit中执行基本相同的操作,但不需要克隆和
返回
@lindapaste,我不知道这种行为。。。非常好!谢谢@当我尝试使用
export const fetchProducts=createAsyncThunk('products/fetchProducts',async(id,{dispatch})=>{return axios.get)时,lindapaste“未经处理的拒绝(TypeError):无法将未定义或null转换为对象”(`https://api-test-carrinho.herokuapp.com/product/business/${id}`)。然后(resp=>{dispatch(addProduct(resp.data))})
但是当我使用addProductS时,它会在控制台中显示一个带有值但为空的警告。我是这样调用函数的:
函数addItemCart(item){dispatch(addItem)(item))console.log('add',item);}
我在哪里调用要添加的函数?我的意思是,我昨天刚开始使用redux。它将是一些
按钮或其他元素的
onClick
属性。
dispatch(addItem(item.\u id))}>add to Cart
以及如何获取产品并设置为addProducts?我使用的是
export const getProducts=(id,index)=>{return dispatch=>{axios.get(`https://api-test-carrinho.herokuapp.com/product/business/${id}`)。然后(res=>{dispatch(addProducts(res.data.data))console.log(res);}).catch(console.log)}}
使用createAsynchRunk-它将处理挂起、完成和错误操作。