首页 > 解决方案 > 无法使用 setState 在反应 js 中更新对象的特定属性

问题描述

我正在获取数据,并且我已映射数据以显示它。一切都很好,但是当我尝试更新获取的数据时(我已经存储在一个数组中,只想通过其他方式更改它,如下所述)不会更新。我已经给出了下面的数据,我只想更改当我单击按钮时分别为每个对象的“数量”属性但是每次我尝试使用扩展运算符并给出一个逗号并尝试在此处定义映射对象的“数量”属性时,它不会更新数量属性并替换整个数组。你能告诉我如何编写一个 onClick 函数来复制整个数组(使用扩展运算符)然后更新数量属性。(获取的数据存储在一个数组中)

0: {id: 11, ordered: true, quantity: 5, user: 1, item: {…}}
1: {id: 12, ordered: true, quantity: 1, user: 1, item: {…}}
2: {id: 14, ordered: true, quantity: 1, user: 1, item: {…}}
3: {id: 15, ordered: true, quantity: 4, user: 1, item: {…}}
4: {id: 17, ordered: true, quantity: 3, user: 1, item: {…}}

这是我正在获取的数据。(上图)

0:
    id: 11
    item: {id: 10, title: "Watch", image: "https://github.com/divanov11/django_ecommerce_mod5/blob/master/static/images/watch.jpg?raw=true", price: "$20"}
    ordered: true
    quantity: 5
    user: 1

它是每个对象中的数据(上图)。我想保持一切不变,只是分别更改每个对象的数量属性。下面是我的代码。

import React, { useEffect, useState } from 'react';

let Cart = () => {

    let [myItems, setMyItems] = useState([])

    let fetchData = () => {
        fetch('http://127.0.0.1:8000/api/get-order-item/' + sessionStorage.getItem('id'), {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer' + ' ' + sessionStorage.getItem('token')
            }
        }).then(
            (response) => {
                return response.json()
            }
        ).then(
            (data) => {
                if (data) {
                    setMyItems(data)
                }
            })
    }

    useEffect(
        () => {
            fetchData()
            console.log(myItems)
        }, []
    )



    return (
        <div>
            {
                myItems.map(
                    (value) => {
                        return (
                            <div className='row  w-100 m-auto' >
                                <div className="card-deck container-fluid m-auto bg-light">
                                    <div className="card  bg-light mt-4 mb-2 " >
                                        <img src={value.item.image} className="card-img-top" alt="..." />
                                        <div className="card-body border-top ">
                                            <div className='d-flex justify-content-between' >
                                                <h4>{value.item.title}</h4>
                                                <div className='d-flex flex-row-reverse' >
                                                    <svg onClick={ () => { setMyItems( (prevState) => { return [...prevState , {value : { quantity : prevState.quantity - 1 }}  ] } )  } } id='add' xmlns="http://www.w3.org/2000/svg" width="28" height="23" fill="currentColor" class="bi bi-plus" viewBox="0 0 16 16" >
                                                        <path fill-rule="evenodd" d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
                                                    </svg>
                                                    <input defaultValue={value.quantity} className='w-25 h-75 text-center' />
                                                    <svg onClick={() => { console.log(myItems) }} xmlns="http://www.w3.org/2000/svg" width="32" height="25" fill="currentColor" class="bi bi-dash" viewBox="0 0 16 16">
                                                        <path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" />
                                                    </svg>
                                                </div>
                                            </div>
                                            <h5>{value.item.price}</h5>
                                            <div class="form-check d-flex  justify-content-between">
                                                <input type="checkbox" class="form-check-input" id="quantity" checked={value.ordered} />
                                                <label class="form-check-label" for="exampleCheck1">Proceed this item to checkout</label>
                                                <button type="button" class="btn btn-success ">Save changes</button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div >
                        )
                    }
                )
            }
        </div >
    )


}

export default Cart;

在映射对象的映射函数内部,我只想创建一个按钮来更改它们各自对象的数量属性

标签: javascriptreactjs

解决方案


尝试是:

setMyItems( (prevState) => { return [...prevState , {value : { quantity : prevState.quantity - 1 }}  ] } )  } }

首先,这很难内联阅读,所以我会将其移至一个函数,以便您了解您正在使用的内容。

接下来,而不是...prevState传播整个前一个数组并用 追加一个新的尾部{value : ...},而是通过索引或 id 找到您想要变异的项目(一种特定于应用程序的决策 - 可能 id 更好)并将数组分成三个件:

  • 数组前面从 0 到i
  • 我们要更改数量的项目,i
  • 数组从后面i + 1到最后

这可以通过切片和扩展来构建一个新数组来完成:

const newArr = [...arr.slice(0, i), arr[i], ...arr.slice(i + 1)];

最后一步:操纵arr[i]物体调整数量。看起来数量在顶层,所以:

{...arr[i], quantity: newQuantity}

综上所述,这是一个可应用于代码的最小可运行示例:

<script type="text/babel" defer>
const {useEffect, useState} = React;

const mockItems = [
  {id: 11, quantity: 5, item: {title: "foo"}},
  {id: 12, quantity: 1, item: {title: "bar"}},
  {id: 14, quantity: 1, item: {title: "baz"}},
  {id: 15, quantity: 4, item: {title: "quux"}},
  {id: 17, quantity: 3, item: {title: "corge"}},
];
fetch = () => new Promise((resolve, reject) => 
  setTimeout(() => 
    resolve({json: async () => mockItems})
  , 1000)
);

const Cart = () => {
  const [items, setItems] = useState([]);

  useEffect(() => {
    (async () => {
      const response = await fetch("some endpoint");
      setItems(await response.json());
    })();
  }, []);
  
  const setQuantity = (id, quantity) => {
    const i = items.findIndex(e => e.id === id);
    setItems(prev => [
      ...prev.slice(0, i),
      {...prev[i], quantity},
      ...prev.slice(i + 1),
    ]);
  };

  return (
    <ul>
      {!items.length ? "loading..." :
        items.map(e =>
          <li key={e.id}>
            <div>{e.item.title}</div>
            <div>
              Qty: {e.quantity}
              <button 
                onClick={() => 
                  setQuantity(e.id, e.quantity + 1)
                }
              >+</button>
              <button 
                onClick={() =>
                  setQuantity(e.id, e.quantity - 1)
                }
              >-</button>
            </div>
          </li>
        )
      }
    </ul>
  );
};

ReactDOM.render(<Cart />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.0/umd/react-dom.production.min.js"></script>


推荐阅读