如何为没有唯一ID的项目生成可预测的反应键?

问题描述 投票:0回答:3

有时,我会在没有唯一ID的情况下映射集合。在这种情况下,react会将此警告记录到控制台:Each child in an array or iterator should have a unique "key" prop.为了解决此问题,我创建了以下es6模块。

const key = {
  count: 0,
  getKey: () => {
    key.count += 1;
    return key.count;
  },
};
export default key;

现在,我在整个React应用程序中导入key模块,并在需要唯一键时调用其getKey方法。但是,我不觉得像React建议的那样产生的密钥是可预测的。虽然第一个渲染可能会以可预测的方式映射具有键1-10的集合,但之后的任何渲染都将为该元素生成新键。使用像UUID这样的包将会产生同样的效果吗?生成可预测的React密钥的正确方法是什么?我是否可以预测他们的意思?谢谢!

reactjs uuid
3个回答
2
投票

在渲染组件之前生成密钥是最终获得可预测密钥的最佳方式。

在理想的世界中,您将使用已经拥有自己唯一ID的对象列表,在这种情况下,最好使用这些ID作为键。

如果您要呈现可能包含重复项的数字列表,则可以在将它们传递给要呈现的组件之前为每个数字提供一个固定键。

let numbers = [1, 1, 2, 3];

let numbersWithKeys = numbers.map(x => {
  return { value: x, key: key.getKey() };
});

function Numbers({ numbers }) {
  return (
    <ul>
     {numbers.map(number => <li key={number.key}>{number.value}</li>)}
    </ul>
  )
}

render(
  <Numbers numbers={numbersWithKeys} />
);

然后,每次组件呈现它都可以安全地为每个数字使用相同的键。您可以想象此组件呈现以下虚拟树:

<ul>
 <li key={1}>1</li>
 <li key={2}>1</li>
 <li key={3}>2</li>
 <li key={4}>3</li>
</ul>

<Numbers>组件可能会反转列表,并且您仍然会为每个项目使用相同的密钥。这就是React可预测的意思。

当您通过移动渲染的元素来更改列表的顺序时,React可以制作一些性能快捷方式,而不是重新渲染整个元素,但如果您在渲染方法中生成键,那么它会看到一个全新的列表每次都必须重新渲染一切。


1
投票

以下是需要考虑的几件事:

  1. 唯一:键必须是唯一的,但仅适用于当前数组。
  2. 可预测:键必须始终映射到同一元素。
  3. 密钥不必是可笑的复杂标识符。

什么是可预测的?如果我们从(index:key):1:1,2:2,3:3开始,我们删除索引2上的元素,我们应该留下:1:1,2:3。索引2和键2的项目已被删除,索引3上的项目已移至索引2,但保留其键3。

最简单的方法当然是在数据中使用唯一属性,例如数据库ID(主键),但这并不总是可行。假设您要创建一个包含可添加或删除的输入字段的动态表单。除了动态分配的唯一标识符之外,输入字段元素可以看起来完全相同。最简单的方法当然是使用map索引,但这是不可预测的,因为如果你删除三个字段中的第二个,它会改变。

我们有什么选择?

一种解决方案是跟踪状态中的元素。

class MyComponent extends Component {
    constructor() {
        super();

        this.state = {
            myArr: []
        };
    }

    _addHandler = el => {
        const arr = this.state[el];
        const length = arr.length;
        const lastId = length > 0 ? arr[length - 1] : 0;

        this.setState({ [el]: [...arr, lastId + 1] });
    };

    _removeHandler = (el, i) => {
        const newItems = [
            ...this.state[el].slice(0, i),
            ...this.state[el].slice(i + 1)
        ];

        this.setState({ [el]: newItems });
    };

    render() {
        const myList = this.state.myArr;

        return (
            <div>
                {myList.map((el, i) => (
                    <p>
                        {this.state.myArr[i]}{" "}
                        <span onClick={() => this._removeHandler("myArr", i)}>
                            remove
                        </span>
                    </p>
                ))}
                <p onClick={() => this._addHandler("myArr")}>Add One</p>
            </div>
        );
    }
}

您可以使用这个概念编写一个简单的包装器组件来跟踪数组中的元素,如果它是您将要做的很多事情。

要分配初始密钥,您可以在componentDidMount()中执行以下操作

componentDidMount() {
    const {someArr} = this.props;
    // use the index to map the initial key values
    // after which use state to track the key pairings
    const newIds = someArr.map((el,i) => i+1);

    this.setState({myArr:newIds});
}

0
投票

您可以使用与项目特别相关的任何数据...或(不推荐)随机ID您可以使用new Date().getTime()密钥用于重用组件...为同一项目提供相同的密钥很重要

   [].map(item, index) => <Item key={'this_list_specific_name' + item.id} />
© www.soinside.com 2019 - 2024. All rights reserved.