如何在减速器中设置状态

How to set state within a reducer

我的 reducer 中有一个产品对象数组,我也有一个空的品牌数组。我想将我的产品对象数组中的所有独特品牌添加到我的减速器中的品牌数组中,有什么办法可以做到吗?

我的减速器:

import * as actionTypes from './shop-types';

 const INITIAL_STATE = {
 products: [
{
  id: 1,
  brand:"DNMX"
},
{
  id: 2,
  brand: "Aeropostale",
},
{
  id: 3,
  brand: "AJIO",
},
{
  id: 4,
  brand: "Nike",
},
],
  cart: [],
  brands: [], //<---- I want to add all the unique brands inside this array
  currentProduct: null,
};

  const shopReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
  case actionTypes.ADD_TO_CART:
  const product = state.products.find(
    (product) => product.id === action.payload.id
  );
  if (product !== null) {
    return {
      ...state,
      cart: state.cart.concat(product),
    };
  } else {
    return null;
  }
default:
  return state;
 }
 };

export default shopReducer;

品牌本质上是派生数据,这意味着它基于或依赖于其他数据。因此,您实际上不需要将其设置为状态,而只需派生即可。

我通常建议使用 Redux Toolkit as it's far simpler, but as you're using old-school Redux, I'd recommend using a library called Reselect。它是一个用于创建可在组件中使用的备忘选择器的库。

对于你的例子,我会尝试这样的事情:

// import the createSelector function 
// we'll use to make a selector
import { createSelector } from "reselect"

// create a "getter". this is just a simplified 
// way of accessing state
const selectBrands = (state) => state.products

// create the selector. this particular selector 
// just looks at `products` in state (from your 
// getter), and filters out duplicate values 
// and returns a unique list
const uniqueBrands = createSelector(selectBrands, (items) =>
  items.filter(
    (item, idx, arr) =>
      arr.findIndex((brand) => brand.name === item.name) === idx
  )
)

然后在您的组件代码中,您可以在 mapStateToProps:

中访问它
const mapStateToProps = (state) => ({
  uniqueBrands: uniqueBrands(state),
})

目前尚未测试,但应该是您要查找的内容。

不清楚您是指购物车还是产品的独特品牌,但它不应该改变您将用来解决此问题的模式。

首先假设产品列表没有变化,您可以简单地将它们添加为初始状态的一部分。

const ALL_PRODUCTS = [
    { id: 1, brand:"DNMX" },
    { id: 2, brand: "Aeropostale" },
    { id: 3, brand: "AJIO" },
    { id: 4, brand: "Nike" }
];

const distinct = (array) => Array.from(new Set((array) => array.map((x) => x.brand)).entries());

const ALL_BRANDS = distinct(ALL_PRODUCTS.map((x) => x.brand));

const INITIAL_STATE = {
    products: ALL_PRODUCTS,
    cart: [],
    brands: ALL_BRANDS, 
    currentProduct: null,
};

如果您要执行添加新产品的操作,并且品牌必须反映您只是在产品状态更新期间应用上述逻辑。

const reducer = (state = INITIAL_STATE, action) => {
    switch(action.type) {
        case (action.type === "ADD_PRODUCT") {
            const products = [...state.products, action.product];

            return {
                ...state,
                products,
                brands: distinct(products.map((x) => x.brand))
            }
        }
    }

    return state;
};

现在采用惯用的方式。您可能会注意到,可以将品牌视为 derived 来自产品集合。这意味着我们甚至可能在状态中不需要它,因为我们可以使用一种叫做选择器的东西从我们的状态中创建派生值,这可以大大简化我们的 reducer/structure 逻辑。

// we dont need brands in here anymore, we can derive.
const INITIAL_STATE = {
  products: ALL_PRODUCTS,
  cart: [],
  currentProduct: null;
};

const selectAllBrands = (state) => {
  return distinct(state.products.map((x) => x.brand))
};

现在当我们 add/remove/edit 新产品时,我们不再需要更新品牌切片。它将源自产品的当前状态。最重要的是,您可以像使用 reducer 一样组合选择器以获得一些非常复杂的逻辑,而不会弄乱您的商店。

const selectCart = (state) => state.cart; 
const selectAllBrands = (state) => {...see above}  
const selectTopBrandInCart = (state) => {
  const cart = selectCart(state);
  const brands = selectBrands(brands);
  
  // find most popular brand in cart and return it.
};

我强烈建议您查看 reselect 以帮助构建可组合和高性能的选择器。