Redux可以接受多个操作

Redux可以接受多个操作,redux,react-redux,Redux,React Redux,AddToCart和RemoveFromCart操作更新数量和数量,而HandleClick和InputQuantity操作更新数量 我对这两行代码有问题: num:productsReducer(state.productsReducer.num,AddToCart) qty:productsReducer(state.productsReducer.qty,AddToCart) 其中,道具数量或数量仅由AddToCart操作更新 其他3个操作-从购物车移除、HandleClick、Input

AddToCartRemoveFromCart操作更新数量和数量,而HandleClickInputQuantity操作更新数量

我对这两行代码有问题:

num:productsReducer(state.productsReducer.num,AddToCart)

qty:productsReducer(state.productsReducer.qty,AddToCart)

其中,道具数量或数量仅由AddToCart操作更新

其他3个操作-从购物车移除、HandleClick、InputQuantity-不更新道具编号或数量。我不知道如何允许上述操作更新prop num和qty

如何编写productsReducer或my操作,使num和qty都由AddToCart和RemoveFromCart更新,而qty由HandleClick和InputQuantity更新

我是否需要一个高阶减速机来处理多个动作?请帮忙

CartContainer

// Both AddToCart and RemoveFromCart actions update num and qty
// Both HandleClick and InputQuantity actions update qty

const mapStateToProps = state => ({
  num: productsReducer(state.productsReducer.num, AddToCart),
  qty: productsReducer(state.productsReducer.qty, AddToCart),
  products: state.productsReducer.products
});
import * as actionTypes from '../actions/actions';

const initialState = {
  products: [],
  num: [],
  qty: [],
  totalPrice: 0,
  status: ''
};

const productsReducer = (state = initialState, action) => {
  let updatedNum = state.num;
  let updatedQty = state.qty;
  let index;

  if (action.num !== undefined) {
    index = updatedNum.indexOf(action.num);
  }

  switch (action.type) {
    case actionTypes.FETCH_PRODUCTS_REQUEST:
      return {
        ...state,
        status: action.status
      }

    case actionTypes.FETCH_PRODUCTS_SUCCESS:
      return {
        ...state,
        status: action.status,
        products: action.products
      }

    case actionTypes.ADD_TO_CART:
      // if num array doesn't have that element, insert that element
      if (!state.num.includes(action.num)) {
        updatedNum = state.num.concat(action.num); 
        updatedQty = state.qty.concat(1);

        return {
          ...state,
          num: updatedNum,
          qty: updatedQty
        }
      // if element is present in array already, increase qty of it by 1
      } else {
        updatedQty = state.qty.slice(0);
        updatedQty[index] += 1;

        return {
          ...state,
          qty: updatedQty
        }
      }

      // After updating store, use CalculatePrice action to calculate updated price.
      this.calculateTotalPrice(updatedNum, updatedQty);

    case actionTypes.REMOVE_FROM_CART:
      // remove clicked item with splice, and update state
      updatedNum.splice(index, 1);
      updatedQty.splice(index, 1);

      return {
        ...state,
        num: updatedNum,
        qty: updatedQty
      }

    case actionTypes.HANDLE_CLICK:
      let event = action.eventType;
      console.log(event);

      if (event === 'plus') {
        updatedQty[index] += 1;
      } else if (event === 'minus') {
        updatedQty[index] -= 1;
      }

      return {
        ...state,
        qty: updatedQty 
      }

    case actionTypes.INPUT_QUANTITY:
      const regex = /^[0-9\b]+$/;

      if (regex.test(action.event.target.value)) {
        updatedQty[index] = Number(action.event.target.value);

        return {
          ...state,
          qty: updatedQty 
        }
      }

    case actionTypes.CALCULATE_PRICE:
      let arr = [];
      console.log('updatedNum', action.num);

      action.num.map(num => {
        let index = num - 1;
        arr.push(action.qty[index] * state.products[index].price);
      });

      if (arr.length > 0) {
        let total = arr.reduce((acc, currentVal) => acc + currentVal);
        return { 
          totalPrice: total 
        }
      } 

      return { 
        totalPrice: 0 
      }

    default: 
      return state;
  }
}

export default productsReducer;
产品减速器

// Both AddToCart and RemoveFromCart actions update num and qty
// Both HandleClick and InputQuantity actions update qty

const mapStateToProps = state => ({
  num: productsReducer(state.productsReducer.num, AddToCart),
  qty: productsReducer(state.productsReducer.qty, AddToCart),
  products: state.productsReducer.products
});
import * as actionTypes from '../actions/actions';

const initialState = {
  products: [],
  num: [],
  qty: [],
  totalPrice: 0,
  status: ''
};

const productsReducer = (state = initialState, action) => {
  let updatedNum = state.num;
  let updatedQty = state.qty;
  let index;

  if (action.num !== undefined) {
    index = updatedNum.indexOf(action.num);
  }

  switch (action.type) {
    case actionTypes.FETCH_PRODUCTS_REQUEST:
      return {
        ...state,
        status: action.status
      }

    case actionTypes.FETCH_PRODUCTS_SUCCESS:
      return {
        ...state,
        status: action.status,
        products: action.products
      }

    case actionTypes.ADD_TO_CART:
      // if num array doesn't have that element, insert that element
      if (!state.num.includes(action.num)) {
        updatedNum = state.num.concat(action.num); 
        updatedQty = state.qty.concat(1);

        return {
          ...state,
          num: updatedNum,
          qty: updatedQty
        }
      // if element is present in array already, increase qty of it by 1
      } else {
        updatedQty = state.qty.slice(0);
        updatedQty[index] += 1;

        return {
          ...state,
          qty: updatedQty
        }
      }

      // After updating store, use CalculatePrice action to calculate updated price.
      this.calculateTotalPrice(updatedNum, updatedQty);

    case actionTypes.REMOVE_FROM_CART:
      // remove clicked item with splice, and update state
      updatedNum.splice(index, 1);
      updatedQty.splice(index, 1);

      return {
        ...state,
        num: updatedNum,
        qty: updatedQty
      }

    case actionTypes.HANDLE_CLICK:
      let event = action.eventType;
      console.log(event);

      if (event === 'plus') {
        updatedQty[index] += 1;
      } else if (event === 'minus') {
        updatedQty[index] -= 1;
      }

      return {
        ...state,
        qty: updatedQty 
      }

    case actionTypes.INPUT_QUANTITY:
      const regex = /^[0-9\b]+$/;

      if (regex.test(action.event.target.value)) {
        updatedQty[index] = Number(action.event.target.value);

        return {
          ...state,
          qty: updatedQty 
        }
      }

    case actionTypes.CALCULATE_PRICE:
      let arr = [];
      console.log('updatedNum', action.num);

      action.num.map(num => {
        let index = num - 1;
        arr.push(action.qty[index] * state.products[index].price);
      });

      if (arr.length > 0) {
        let total = arr.reduce((acc, currentVal) => acc + currentVal);
        return { 
          totalPrice: total 
        }
      } 

      return { 
        totalPrice: 0 
      }

    default: 
      return state;
  }
}

export default productsReducer;

所以我会给你两个答案,第一个主要是解释为什么只有AddToChart更新道具,而不是其他动作,第二个主要是一组由你来实施的建议

1)为什么只有AddToChart更新道具(redux状态)

请在下面找到同样适用于您的Redux官方文件中的引用

为什么我的组件没有重新渲染,或者我的MapStateTrops没有运行? 到目前为止,意外地直接改变或修改您的状态是最重要的 操作完成后组件不重新渲染的最常见原因 被派遣

这句话的重要部分是

意外地直接改变或修改您的状态

让我们来看看这是如何适用于你的

在初始状态下,您具有以下值

const initialState = {
  num: [],
  qty: []
};
num和qty字段是数组

在ProductReducer的开头,您还有以下行

let updatedNum = state.num;
let updatedQty = state.qty;
let index;
在进一步讨论之前,让我们回顾一下javascript中数组的特殊之处是什么

请从这篇文章中找到一条引文

也许您已经注意到,如果将一个数组分配给另一个数组 变量并修改该变量的数组元素,即原始数组 也进行了修改。或者您可能已将数组传递给函数 希望只修改数组的本地副本,但您发现 原始阵列也已修改。这是因为数组 是JavaScript中的引用类型

这意味着如果将数组分配给变量或传递数组 对于函数,它是对原始数组的引用 已复制或传递,而不是数组的值

因此,updatedNum和updatedQty基本上不是新数组,而是对原始数组的引用,因此,每当您修改updatedNum和updatedQty时,它们所引用的原始数组也会更新

在我们的例子中,原始数组实际上就是我们在状态中拥有的数组,所以基本上只要有更新,原始状态就会发生变化,正如第一句话所说

意外突变可防止在执行操作后重新渲染 派遣

将添加到购物车中

case actionTypes.ADD_TO_CART:
   ...
   updatedNum = state.num.concat(action.num); 
   updatedQty = state.qty.concat(1);
使用concat()方法

为什么它对你有用

来自MDN

concat()方法用于合并两个或多个数组。这种方法 不更改现有数组,而是返回一个新数组

所以没有意外的变异,你返回一个全新的数组

从图表中删除\u
方法

case actionTypes.REMOVE_FROM_CART:
      updatedNum.splice(index, 1);
      updatedQty.splice(index, 1);
使用splice()方法

来自MDN

splice()方法通过删除或替换现有元素和/或添加新元素来更改数组的内容

这是一个变异,因为不是返回新数组,而是变异引用到状态的原始数组

HANDLE\中单击
更新数量

updatedQty[index] -= 1;
通过这种方式,再次对数组进行变异,而不是返回一个全新的数组,因此可以对引用到状态的原始数组进行变异

在当前情况下,如果执行以下更新

let updatedNum = state.num.concat();
let updatedQty = state.qty.concat();
//I would suggest you to use spread operator
let updatedNum = [...state.num]
let updatedQty = [...state.qty]
你最初的大部分问题都会解决

2)如何编写productsReducer或my actions,使num和qty都得到更新

对于这个问题,我不能给你们具体的分步指导,因为如何构建你们的商店应该由你们根据应用的当前需求和未来可能的改进来决定

不过,我可以提出几点建议

首先,我可能会去掉下面的部分

 let updatedNum = state.num;
  let updatedQty = state.qty;
  let index;

  if (action.num !== undefined) {
    index = updatedNum.indexOf(action.num);
  }
对我来说,我通常在一个孤立的作用域中在我的操作处理程序中处理这种检查和赋值,因为当代码变得更大时,我很难去看我在哪里声明这些变量,其次,你永远无法确定它们是否发生了变异,使调试变得更困难。另外,在将来,您可能需要为某些操作处理程序更新updateNum的声明逻辑。如果您有这种声明,您甚至可以在没有意识到的情况下破坏应用程序的某些部分

其次,我认为下面的用法是一种反模式用法

// After updating store, use CalculatePrice action to calculate updated price.
      this.calculateTotalPrice(updatedNum, updatedQty);
如果您的意图是在操作处理程序内部触发此函数,那么它将不起作用,因为在上面的行中,您已经返回了一条语句

如果您打算在
添加图表
之后更新价格,
从图表中删除

case actionTypes.ADD_TO_CART:
   ...
   updatedNum = state.num.concat(action.num); 
   updatedQty = state.qty.concat(1);
您可以将该计算逻辑放入这些操作处理程序中,进行计算并返回新状态

比如说

...
const totalPrice = Some logic related to updatedNum and updateQty
return {
          ...state,
          num: updatedNum,
          qty: updatedQty,
          totalPrice
        }
我希望我的回答能帮助你设计一条更清晰的道路。通常有两种不同的方法可以创建动作、动作缩减器等

然而,对r来说最重要的是
if (typeof nextStateForKey === 'undefined') {
  const errorMessage = getUndefinedStateErrorMessage(key, action)
  throw new Error(errorMessage)
}
hasChanged = hasChanged || nextStateForKey !== previousStateForKey