简体   繁体   English

如何在减速机内设置 state

[英]How to set state within a reducer

I have an array of product objects inside my reducer and I have an empty array of brand as well.我的减速器中有一系列产品对象,我也有一个空的品牌数组。 I want to add all the unique brands from my products object array into my brands array in my reducer, is there any way I can do that?我想将我的产品 object 阵列中的所有独特品牌添加到我的减速器中的品牌阵列中,有什么办法可以做到吗?

My 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;

Brands is essentially derived data, meaning it's based off, or reliant on other data.品牌本质上是派生数据,这意味着它基于或依赖于其他数据。 Because of this, you don't actually need to set it in state, and instead rather, just derive it.因此,您实际上不需要在 state 中设置它,而是直接派生它。

I'd normally recommend using Redux Toolkit as it's far simpler, but as you're using old-school Redux, I'd recommend using a library called Reselect .我通常建议使用Redux 工具包,因为它要简单得多,但由于您使用的是老式 Redux,我建议使用名为Reselect的库。 It's a library for creating memoized selectors that you can consume in your component.它是一个用于创建可在组件中使用的记忆选择器的库。

For your example, I'd try something like:对于您的示例,我会尝试以下操作:

// 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
  )
)

Then in your component code, you can access this in mapStateToProps :然后在您的组件代码中,您可以在mapStateToProps中访问它:

const mapStateToProps = (state) => ({
  uniqueBrands: uniqueBrands(state),
})

This is currently untested, but should be what you're looking for.这目前未经测试,但应该是您正在寻找的。

It's not really clear if you mean unique brands by cart or products, but it shouldn't change the patterns you'll use to solve this.不清楚您是指购物车或产品的独特品牌,但它不应该改变您用来解决这个问题的模式。

First assuming the product list isn't changing, you can simply add them as part of the initial state.首先假设产品列表没有改变,您可以简单地将它们添加为初始 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,
};

If you will have an action that will add a new products and the brands have to reflect that you just apply the above logic during the state updates for the products.如果您将执行添加新产品的操作,并且品牌必须反映您只是在产品的 state 更新期间应用上述逻辑。

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;
};

Now for the idiomatic way.现在是惯用的方式。 You might note that brands can be considered derived from the products collection.您可能会注意到,品牌可以被认为是从产品系列中derived出来的。 Meaning we probably dont even need it in state since we can use something called a selector to create derived values from our state which can greatly simplify our reducer/structure logic.这意味着我们可能在 state 中甚至不需要它,因为我们可以使用称为选择器的东西从我们的 state 创建派生值,这可以大大简化我们的减速器/结构逻辑。

// 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))
};

Now when we add/remove/edit new products we no longer need to update the brand slice.现在,当我们添加/删除/编辑新产品时,我们不再需要更新品牌切片。 It will be derived from the current state of products.它将衍生自当前state的产品。 On top of all of that, you can compose selectors just like you can with reducers to get some really complex logic without mucking up your store.最重要的是,您可以像使用 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.
};

I would highly recommend you check out reselect to help build composable and performant selectors.我强烈建议您查看reselect以帮助构建可组合和高性能的选择器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM