【问题标题】:React - unique key in object gets overwritten when updating state arrayReact - 更新状态数组时对象中的唯一键被覆盖
【发布时间】:2019-12-19 16:01:32
【问题描述】:

我有一个将商品添加到购物车的功能。单击按钮时,我正在尝试为新对象项生成唯一键。我有new Date().getTime() 根据点击频率执行此操作。我在函数内部测试了console.log,它为每个新对象生成不同的unique_id 数字。当我尝试将现有的对象状态数组与新的 item 对象组合时,问题就开始了。所有现有的unique_ids 都被覆盖为最新的unique_id。我还在为状态数组使用 React useState 钩子。 React 有点相关,因为状态数组不应该直接编辑,而是通过 setter 方法。

我尝试了.push.concatArray.from、扩展运算符、循环和在函数内外分配变量的组合。我知道这是 pass-by-referencepass-by-value 的情况,因为这些数组方法只是浅拷贝。

const [cart, setCart] = useState([])

const addItem = (item) => {
   let cartArr = cart
   item['unique_id'] = new Date().getTime()
   setCart([...cartArr, item])
}

预期:

[
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696123},
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696256},
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696377}
]

但是得到了:

[
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696377},
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696377},
{id: 1, price: 10.11, title: "The Art Of War", unique_id: 1565675696377}
]

【问题讨论】:

  • 我能知道,cartArr 变量在哪里定义?
  • 我忘了包括作业:let cartArr = cart
  • 你能尝试创建一个简单的 React sn-p 来重现这个问题吗?
  • 这取决于您拨打addItem() 的频率。能否请您在addItem() 函数中记录时间以隔离问题?
  • 这样做会使 state 的 cart 属性不可变。 let cartArr = [...cart]

标签: javascript reactjs multidimensional-array reference javascript-objects


【解决方案1】:

您可以将数组和对象连接起来,而不是推送数组中的值,请参阅下面的代码供您参考

  const [cart, setCart] = useState([]);
  const addItem = item => {
    item["unique_id"] = new Date().getTime();
    setCart([...cart, item]);
  };

这里是codesandbox demo

【讨论】:

    【解决方案2】:

    正如我在评论中提到的,这取决于您拨打addItem 的频率。 下面的 sn-p 显示如果您连续调用 addItem,所有项目都会在同一时间(几乎总是):

    let cart = [];
    
    const addItem = (item) => {
        let cartArr = cart;
        let currentTime = new Date().getTime();
        item['unique_id'] = currentTime;
        cartArr.push(item);
        cart = cartArr;
    }
    addItem({ price: 10 });
    addItem({ price: 11 });
    addItem({ price: 12 });
    console.log(cart);

    但是,通过引入console.log 增加了一些延迟后,时间戳是不同的(不能保证):

    let cart = [];
    
    const addItem = (item) => {
        let cartArr = cart;
        let currentTime = new Date().getTime();
        console.log('Price: ' + item.price);
        console.log('Current time: ' + currentTime);
        item['unique_id'] = currentTime;
        cartArr.push(item);
        cart = cartArr;
    }
    addItem({ price: 10 });
    addItem({ price: 11 });
    addItem({ price: 12 });
    console.log(cart);

    所以,这里我认为 React 状态与时间戳或数组操作无关。

    【讨论】:

      【解决方案3】:

      这个问题与 React 无关。执行的循环恰好在同一个时间戳内完成。

      我建议不要通过循环模拟在购物车中添加多个商品。

      var arr = [];
      
      for(let i = 0; i < 10; i++){
        arr.push(new Date().getTime())
      }
      
      console.log(arr);

      【讨论】:

        【解决方案4】:

        到目前为止,您会发现,时间戳作为键很糟糕。

        重要 - React 需要 STABLE 键,键的分配应该完成一次,每个项目每次都应该获得相同的键,这样 React 才能发挥其价值的魔力更改并决定何时重新渲染。 但是new Date函数你永远不会给你STABLE键,总是不一致。每个新的函数值都会给出唯一的键。 Unique 应该针对有问题的组件,而不是全局应用程序,以避免重新渲染该组件。

        始终最好使用数据库中的一些标识符,例如 id

        如果不可用 我建议使用shortid 来生成密钥。

        步骤:-

        1) npm install shortid --save

        2) import shortid from 'shortid';

        3) 用法

        <li key={shortid.generate()}>
          {value}
        </li>
        

        希望对你有帮助!!!

        【讨论】:

          【解决方案5】:

          正如@fiveelements 所说,由于Date.now() 不是生成随机值的可靠方法,因此您会遇到ID 冲突。幸运的是,浏览器有一个新功能正好可以生成随机值。您可以尝试将其用于您的 id:

          const id = window.crypto.getRandomValues(new Uint32Array(10))[0];
          console.log(id);

          您可以阅读有关window.crypto object on MDN 的更多信息。所有现代浏览器都支持它,包括 IE11:https://caniuse.com/#search=crypto

          如果您从某个地方(例如数据库)获取该数据,您可能已经拥有某种可以使用的唯一 ID。如果您可以在渲染中保持keys 的一致性,它将通过避免不必要的重新渲染来提高性能。但如果您没有任何 id,这个 sn-p 可以帮助您即时生成它们。

          您也可以查看CodePen

          【讨论】:

            【解决方案6】:

            我用 Object.assign() 解决了这个问题。它也不需要额外的cartArr 变量。

            const [cart, setCart] = useState([])
            
            const addItem = (item) => {
                item['unique_id'] = new Date().getTime()
                let newItem = Object.assign({}, item)
                setCart([...cart, newItem])
            }
            

            【讨论】:

              猜你喜欢
              • 2021-09-05
              • 2016-10-06
              • 1970-01-01
              • 2020-06-02
              • 2020-10-21
              • 2019-06-15
              • 1970-01-01
              • 1970-01-01
              • 2020-05-26
              相关资源
              最近更新 更多