【问题标题】:In REACTJS, same function has different state depending from where (in the dom) is called在 REACTJS 中,相同的函数具有不同的状态,具体取决于调用位置(在 dom 中)
【发布时间】:2021-05-26 07:17:59
【问题描述】:

让我开始说我知道 setState 是异步的,这不是我遇到的问题(我猜)!

我有一个表,我将每一行存储在一个数组中,定义为:

const [rows, setRows] = useState([]);

我将表格渲染为:

rows.map(r => {
   if(r.prop == 1) {
      return (
         <div key={r.position} onClick={e => AddRow(r.position)}>{r.label} </div>
      )
   }
})

AddRow 函数只是在指定索引 (r.position) 处添加一行,并相应地移动和更新所有行。当然,我使用 setRows([...newRows]); 更新行;

只要我在底部添加一行,它就可以完美运行。但是,例如,如果我在位置 2 添加一行并且行数为 10,则行(数组)仅包含 2 行,就像添加第二行时一样。第 3 行、第 4 行也一样.....就像 AddRow 函数已为每一行复制,并在复制时使用“行”的状态。

我尝试删除 if 语句,但问题仍然存在。我也尝试使用假 id 作为密钥,但问题仍然存在。

我感觉我错过了一些非常简单的东西,有什么想法吗?

非常感谢

供参考,这是 AddRow 函数

const AddTile = (atPosition) => {
        let newPosition = atPosition + 1;

        let newRow = {
            position: newPosition,
            label: 'something',
            prop: 1
        }        

        let tmp = [];
        let counter = 0;
        let added =  false;
        for(let t of rows) {
            if(newPosition == counter) {
                tmp.push(newRow);
                counter++;
                added = true;
            } 
            
            t.position = counter;
            tmp.push(t);
            counter++;
        }

        if(!added) {
            tmp.push(newTile);
        }

        setRows([...tmp]);
  }

【问题讨论】:

  • 其实setState不是async,是一个完全同步的函数。 React 状态更新是异步的,即它们被排队和异步处理。 addTileaddRow的功能一样吗?
  • 另外,如果你在 Array 中插入一些东西,我们可以使用 array index 来跟踪位置。我不认为我们明确需要 position 属性。

标签: reactjs


【解决方案1】:

问题

问题似乎是您使用元素 position 属性作为反应键,因此键不稳定,这意味着数据没有“粘”到它们的 React 键,因此 React 知道它是上一次渲染中的相同对象。

解决方案

我建议将“索引”/“计数器”/“位置”与组件分离,作为识别它并用作 React 键的一种方式。使用与元素保持一致的 GUID,并使用映射的 index 作为插入位置。您还应该使用setRows 中的功能状态更新来更新之前的状态,而不是 some 状态,该状态在回调范围内从创建回调的渲染周期中关闭。 (几乎任何时候状态更新取决于您想要使用功能更新的任何先前状态。

import { v4 as uuidV4 } from 'uuid';

...

const addRow = (index) => {
  const newRow = {
    id: uuidV4(),       // <-- new id
    label: 'something',
    prop: 1
  }

  setRows(rows => {
    const newRows = rows.slice();     // <-- copy previous state
    newRows.splice(index, 0, newRow); // <-- insert after index
    return newRows;                   // <-- return new state
  });
}

...

rows.map((r, index) => (
  <div
    key={r.id}                    // <-- use guid as key
    onClick={() => addRow(index)} // <-- use index for insertion
  >
    {r.label}
  </div>
))

此外,您似乎一直在尝试根据prop 值进行一些过滤。如果是这样,那么先做过滤器,然后映射。

rows
  .filter(r => r.prop === 1)
  .map((r, index) => (
    <div key={r.id} onClick={() => addRow(index)} >
      {r.label}
    </div>
  ))

【讨论】:

  • 是的,这就是问题所在!我认为这可能是问题所在,我曾尝试使用计数器作为键......只是错误的解决方案!我为每个元素添加了一个实际的唯一 ID,一切正常!非常感谢!
  • 对不起 Drew,但我错了,只有 50% 的问题出在 key 上,另外 50% 出在 setRows 上。将函数传递给 setRows (添加新行,就像您在代码中所做的那样)而不是传递已经更新的新数组(就像我在我的那样)解决了这个问题。我猜想使用函数会强制 REACT 去检索当前状态或行。
  • @Mark 啊,是的,我明白了...我没有明确指出,但通常在 React 状态下更新数组时,您希望使用功能状态更新,因此您从以前的状态而不是在创建回调的渲染周期中的状态。它有助于避免状态的陈旧外壳。基本上任何时候 next 状态都取决于您想要使用功能更新的任何 previous 状态。
猜你喜欢
  • 2015-12-07
  • 1970-01-01
  • 2013-05-10
  • 1970-01-01
  • 2014-05-31
  • 2019-07-14
  • 2017-07-01
  • 2017-04-08
  • 1970-01-01
相关资源
最近更新 更多