在React.js中推送唯一对象

时间:2018-06-02 00:32:30

标签: javascript arrays reactjs object

我正在构建一个非常有缺陷的电子商务应用程序,使用React只是为了好玩,我试图找出如何在某个对象被推入阵列后设置状态。

我有一个$('#option1, #option2, #option3').on('click', function() { $('#contact_form').bootstrapValidator('enableFieldValidators', 'Truck Options', false); }); $('#option4').on('click', function() { $('#contact_form').bootstrapValidator('enableFieldValidators', 'Truck Options', true); }); 数组,我从我最初的cart数组中添加了我保留所有产品的项目。

不确定的是,我有产品库存。所以,让我们说我的items产品有5个库存,每当我推送chocolate对象时,它就会堆积并在购物车中添加相同的商品:

enter image description here

我想将chocolate对象推送到购物车阵列,但如果已经存在,我不想要复制。相反,我希望实现添加chocolates对象的内容,但每次添加时它的数量都会相应更改。它看起来像这样:

enter image description here

我怎样才能实现这样的目标?也许检查一下这个对象是否已经添加到chocolate数组中,然后是不是渲染副本,只需按下值并更新该项的数量?

被困了几个小时,非常感谢一些提示。

cart

3 个答案:

答案 0 :(得分:3)

通过ID将您的产品存储在常规对象中。

1: {
    id: 1,
    name: 'chocolate'
}

将您的购物车存储为一系列ID。

[1, 1, 1]

在您的组件中,按ID分组ID购物车数组以获取计数,并按ID查找购物车对象以获取其数据。

应计算计算数据,而不是存储。

这里有一些完全未经测试的,无条件的代码,显示了在渲染函数中完成的计算:

class App extends Component {
    state = {
        cart: [],
        items: [
            { id: uuid(), name: 'chocolate', price: 10, available: 5 },
            { id: uuid(), name: 'strawberry', price: 50, available: 10 },
            { id: uuid(), name: 'banana', price: 43, available: 20 }
        // Convert to an object of { id: { id, name, price } }
        ].reduce((memo, item) => ({
            ...memo,
            [item.id]: item
        }), {}),
    }

    addItem = id => {
        const { cart, } = this.state

        this.setState({
            cart: [ ...cart, id ]
        })
    }

    removeItem = removeId => {
        const { cart, } = this.state
        this.setState({
            cart: cart.filter(({ id }) => id !== removeId)
        })
    }

    render() {
        const { cart, items, total, addToCartMessage, removeFromCartMessage } = this.state

        // Given an array of item IDs in our cart, group them into an object
        // with the total count and price for each item, and overall count
        const accumulatedItems = items.reduce((memo, item) => {
            const { id, price } = item;
            const { count, price, } = memo[id] || {};
            return {
                ...memo,
                cartTotal: memo.cartTotal + price,
                [id]: {
                    count: (count || 0) + 1,
                    total: (price || 0) + price,
                }
            };
        // Starting object to reduce
        }, {
            cartTotal: 0,
        });

        return (
            <div className="App">
                {cart.length === 0 ? <h3>No items in cart</h3> : (
                    <div>
                        <h1>Cart:</h1>
                        {Object.keys(accumulatedItems).sort().map(id => (
                            <div key={id}>
                                <h1>{items[id].name} x {accumulatedItems[id].total}</h1>
                                <p>${accumulatedItems[id].total}</p>
                                <button onClick={() => this.removeItem(id)}>Remove From Cart</button>
                            </div>
                        ))}
                    </div>
                )}
            </div>
        );
    }
}

处理计算数据,并像remaining一样改变状态,为您的应用增加了显着的逻辑复杂性。一旦出现性能问题,您应该开始担心尝试存储/缓存/记忆计算状态。计算渲染函数中的总计是第一次通过。

在React中,有一些像reselect这样的解决方案,在技术上不需要redux。它们只是缓存处于给定状态的函数,并生成计算输出,并且只有在输入发生变化时才重新计算输出。

答案 1 :(得分:1)

也许你可以在你的问题中思考和解释。有多种方法可以做到这一点,每个人都喜欢这样做,或者他们多么容易做到和维护它。

  • 您可以使用项目的唯一ID为数组中的每个项目保留一个对象,而不是插入项目本身。那个对象也可以容纳数量。然后,您可以通过此唯一ID生成卡信息。

示例:

cart: [
          { id: uniqeId, quantiy: 1 },
          { id: uniqeId, quantiy: 6 },
      ]

将商品添加到卡片后,您可以直接更改相关对象的数量。但为此,您必须在此数组中找到该项目,然后根据您的猜测更改数量。

  • 您可以将购物车对象中的商品ID(此次不是数组)作为数组,但这次您将数量分开并按项目ID将其保存为对象。因此,在将项目添加到购物车的ID数组列表后,您还可以更改项目对象的数量。使用这种方法,你不必费力地找到任何东西,但你需要改变两条信息。

像:

cart: {
          ids: [ uniqueId, uniqueId ],
          quantity: { uniqueId: 1, uniqueId: 6 }
      } 
  • 或者您可以按照您的描述进行操作,只需添加项目,但在此之前检查项目是否已经存在。例如按id过滤。但是,使用这种技术可能会出现一些问题。当您添加这样的项目时,例如价格,剩余等,您还必须使用项目状态维护购物车状态。例如,当您想要更改商品的价格时会发生什么?或者,如果有另一种方式(以某种方式)改变剩下的其他方式然后将项目添加到购物车?但是,如果您只使用ID,则可以从单个状态中提取这些信息:items。

但我也是一个学习者,也许有更好的方法除了那些。我还没有写过购物车,但如果我这样做了,我可能会使用第二种方法。

顺便说一下,不要用push来改变你的状态。这会改变你的状态,这是不可取的。使用类似concat或扩展运算符的内容来创建新状态。

item = { foo: bar, fizz: buzz }
// now setting state
cart: [ ...this.state.cart, item ]

如果状态更改取决于先前的状态,请尝试在setState中使用回调函数(因为它是异步操作)。

this.setState( prevState => ( {
    cart: [ ...prevState.cart, item ],
} ) )

答案 2 :(得分:0)

使用@devserkan重建购物车状态的建议和@Andy Ray关于重组商品状态的建议,我设置我的状态看起来像这样:

state = {
  items: {
    1: {
      id: 1, name: 'chocolate', price: 10, available: 5
    },  

    2: {
      id: 2, name: 'strawberry', price: 10, available: 10
    },

    3: {
      id: 3, name: 'banana', price: 10, available: 20
    }
  }

  cart: {
    ids: [],
    quantity: { 1: 0, 2: 0, 3: 0 }
  }
}

然后我去渲染了这些项目并添加了一个onClick函数(addItem)来处理一些setState调用:

render() {
  const { cart, items } = this.state

return (
  <div>
    <h1>Shopping Area</h1>
    {Object.values(items).map(item => (
      <div key={item.id}>
        <h2>{item.name}</h2>
        <h2>{item.price}</h2>
        <button onClick={() => this.addItem(item)}>Add To Cart</button>
      </div>
    ))}

在我的addItem函数中,我继续设置状态cart,以便我按下商品ID,并更新该ID的数量:

addItem = item => {
  const { cart, items } = this.state

  this.setState({
    cart: {
      ...cart,
      // Push item id to ids array inside cart state
      ids: [...cart.ids, item.id],
      quantity: {
        ...cart.quantity,
        // Update quantity of the specific id pushed by 1
        [item.id]: cart.quantity[item.id] + 1
      }
    }
  })
}

最后我必须渲染购物车部分:我这样做是通过检查cart.ids数组是否为空并进行另一次检查以仅渲染数量大于0的项目。如果我们没有进行检查,每次我们推送一个项目时,它会同时添加所有3个,我们只希望显示该特定项目。

 {cart.ids.length !== 0 ? Object.keys(items).map(id => (
   <div key={id}>
     // Check to see if quantity for that item is > 0
     {cart.quantity[id] > 0 && (
       <h1>{items[id].name} x {cart.quantity[id]}</h1>
     )}
   </div>
 )) : <h1>No Items In Your Cart</h1>}

完整代码(无价格/剩余)

export default class App extends Component {
  state = {
    cart: {
      ids: [],
      quantity: {
        1: 0, 
        2: 0, 
        3: 0
      }
    },

    items: {
      1: {
        id: 1, name: 'chocolate', price: 10, available: 5
      },  

      2: {
        id: 2, name: 'strawberry', price: 10, available: 10
      },

      3: {
        id: 3, name: 'banana', price: 10, available: 20
      }
    }
  }

  addItem = item => {
    const { cart, items } = this.state

    this.setState({
      cart: {
        ...cart,
        ids: [...cart.ids, item.id],
        quantity: {
          ...cart.quantity,
          [item.id]: cart.quantity[item.id] + 1
        }
      }
    })
  }

  removeItem = removeId => {
    const { cart } = this.state
    this.setState({
      cart: cart.filter(({ id }) => id !== removeId)
    })
  }

  render() {
    const { cart, items, total, addToCartMessage, removeFromCartMessage } = 
    this.state

    return (
      <div className="App">
        <h1>Shopping Area</h1>
        {Object.values(items).map(item => (
          <div key={item.id}>
            <h2>{item.name}</h2>
            <h2>{item.price}</h2>
            <button onClick={() => this.addItem(item)}>Add To Cart</button>
          </div>
        ))}

        <hr style={{'marginTop': '200px'}} />

        <h1>Cart</h1>

        {cart.ids.length !== 0 ? Object.keys(items).map(id => (
          <div key={id}>
            {cart.quantity[id] > 0 && (
              <h1>{items[id].name} x {cart.quantity[id]}</h1>
            )}
          </div>
        )) : <h1>No Items In Your Cart</h1>}
      </div>
    )
  }
}

非常感谢@Andy Ray和@devserkan的建议。