Как создать уникальные ключи для элементов React?

Я делаю приложение React, которое позволяет вам создавать список и сохранять его, но React предупреждает меня, что у моих элементов нет уникальной подсказки ключа (элементы List/ListForm). Как создать уникальную подсказку для созданных пользователем элементов? Ниже мой код React

var TitleForm = React.createClass({
    handleSubmit: function(e) {
        e.preventDefault();
        var listName = {'name':this.refs.listName.value};
        this.props.handleCreate(listName);
        this.refs.listName.value = "";
    },
    render: function() {
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <input className='form-control list-input' type='text' ref='listName' placeholder="List Name"/>
                    <br/>
                    <button className="btn btn-primary" type="submit">Create</button>
                </form>
            </div>
        );
    }
});

var ListForm = React.createClass({
    getInitialState: function() {
        return {items:[{'name':'item1'}],itemCount:1};
    },
    handleSubmit: function(e) {
        e.preventDefault();
        var list = {'name': this.props.name, 'data':[]};
        var items = this.state.items;
        for (var i = 1; i < items.length; i++) {
            list.data.push(this.refs[items[i].name]);
        }
        this.props.update(list);
        $('#'+this.props.name).remove();
    }, 
    handleClick: function() {
        this.setState({
            items: this.state.items.concat({'name':'item'+this.state.itemCount+1}),
            itemCount: this.state.itemCount+1
        });
    },
    handleDelete: function() {
        this.setState({
            itemCount: this.state.itemCount-1
        });
    },
    render: function() {
        var listItems = this.state.items.map(function(item) {
            return (
                <div>
                    <input type="text" className="list-form" placeholder="List Item" ref={item.name}/>
                    <br/>
                </div>
            );
        });
        return (
            <div>
                <form onSubmit={this.handleSubmit} className="well list-form-container">
                    {listItems}
                    <br/>
                    <div onClick={this.handleClick} className="btn btn-primary list-button">Add</div>
                    <div onClick={this.handleDelete} className="btn btn-primary list-button">Delete</div>
                    <button type="submit" className="btn btn-primary list-button">Save</button>
                </form>
            </div>
        )
    }
});


var List = React.createClass({
    getInitialState: function() {
        return {lists:[], savedLists: []};
    },
    handleCreate: function(listName) {
        this.setState({
            lists: this.state.lists.concat(listName)
        });
    },
    updateSaved: function(list) {
        this.setState({
            savedLists: this.state.savedLists.concat(list)
        });
    },
    render: function() {
        var lst = this;
        var lists = this.state.lists.map(function(list) {
            return(
                <div>
                    <div key={list.name} id={list.name}>
                        <h2 key={"header"+list.name}>{list.name}</h2>
                        <ListForm update={lst.updateSaved} name={list.name}/>
                    </div>
                </div>
            )
        });
        var savedLists = this.state.savedLists.map(function(list) {
            var list_data = list.data;
            list_data.map(function(data) {
                return (
                    <li>{data}</li>
                )
            });
            return(
                <div>
                    <h2>{list.name}</h2>
                    <ul>
                        {list_data}
                    </ul>
                </div>
            )
        });
        var save_msg;
        if(savedLists.length == 0){
            save_msg = 'No Saved Lists';
        }else{
            save_msg = 'Saved Lists';
        }
        return (
            <div>
                <TitleForm handleCreate={this.handleCreate} />
                {lists}
                <h2>{save_msg}</h2>
                {savedLists}
            </div>
        )
    }
});

ReactDOM.render(<List/>,document.getElementById('app'));

Мой HTML:

<div class="container">
    <h1>Title</h1>
    <div id="app" class="center"></div>
</div>

Ответ 1

Существует множество способов создания unique keys, самым простым методом является использование индекса при повторении массивов.

пример

    var lists = this.state.lists.map(function(list, index) {
        return(
            <div key={index}>
                <div key={list.name} id={list.name}>
                    <h2 key={"header"+list.name}>{list.name}</h2>
                    <ListForm update={lst.updateSaved} name={list.name}/>
                </div>
            </div>
        )
    });

Везде, где вы перебираете данные, здесь this.state.lists.map вы можете передать вторую function(list, index) параметра function(list, index) на обратный вызов, и это будет его значение index и оно будет уникальным для всех элементов в массив.

И тогда вы можете использовать его как

<div key={index}>

Вы можете сделать то же самое здесь

    var savedLists = this.state.savedLists.map(function(list, index) {
        var list_data = list.data;
        list_data.map(function(data, index) {
            return (
                <li key={index}>{data}</li>
            )
        });
        return(
            <div key={index}>
                <h2>{list.name}</h2>
                <ul>
                    {list_data}
                </ul>
            </div>
        )
    });

редактировать

Однако, как указал пользователь Martin Dawson в комментарии ниже, это не всегда идеально.

Так что же тогда решение?

Многие

  • Вы можете создать функцию для генерации уникальных ключей/идентификаторов/номеров/строк и использовать ее
  • Вы можете использовать существующие пакеты npm, такие как uuid, uniqid и т.д.
  • Вы также можете генерировать случайные числа, такие как new Date().getTime(); и префикс его с чем-то из предмета, который вы итерируете, чтобы гарантировать его уникальность
  • Наконец, я рекомендую использовать уникальный идентификатор, который вы получаете из базы данных, если вы его получите.

Пример:

const generateKey = (pre) => {
    return '${ pre }_${ new Date().getTime() }';
}

const savedLists = this.state.savedLists.map( list => {
    const list_data = list.data.map( data => <li key={ generateKey(data) }>{ data }</li> );
    return(
        <div key={ generateKey(list.name) }>
            <h2>{ list.name }</h2>
            <ul>
                { list_data }
            </ul>
        </div>
    )
});

Ответ 2

Для этого можно использовать пакет uuid npm.

Общий модуль Nodejs

const uuidv1 = require('uuid/v1');
uuidv1(); // '10ba038e-48da-487b-96e8-8d3b99b6d18a

С ecma6:

import uuidv1 from  'uuid/v1';
uuidv1(); // '10ba038e-48da-487b-96e8-8d3b99b6d18a

Ответ 3

Как упоминалось ранее, использование индекса является антипаттерном. Лучший вариант, который я нашел до сих пор:

1) Импортировать "shorttid" из библиотеки React:

import shortid from 'shortid';

2) Используйте его в вашей петле, как показано ниже:

const controls = [];
collection.map(item => {
    controls.push(<div className="column" key={'div-${shortid.generate()}'}>{item.text}</div>);
});

Надеюсь это поможет. Я только что опубликовал это, потому что было действительно трудно найти решение, подобное этому.

Ответ 4

Важно помнить, что React ожидает ключи STABLE, то есть вы должны назначать ключи один раз, и каждый элемент в вашем списке должен получать один и тот же ключ каждый раз, таким образом React может оптимизировать изменения ваших данных при согласовании виртуального DOM и принять решение какие компоненты нужно перерисовать. Итак, если вы используете UUID, вам нужно сделать это на уровне данных, а не на уровне UI.

Также имейте в виду, что для ключа можно использовать любую строку, которую вы хотите, поэтому вы часто можете объединять несколько полей в один уникальный идентификатор, например, ${username}_${timestamp} может быть прекрасным уникальным ключом для строки в чате., например.

Ответ 5

Обновление ответа с v4 UUID

npm install uuid
-------------------------------------
const uuidv4 = require('uuid/v4');
uuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'

or

import uuidv from  'uuid/v4';
uuidv4(); // '10ba038e-48da-487b-96e8-8d3b99b6d18a

Ответ 6

Не используйте этот return $ {pre} _ $ {new Date(). GetTime()} ; , Вместо этого лучше иметь индекс массива, потому что, хотя он и не идеален, таким образом вы, по крайней мере, получите некоторую согласованность между компонентами списка, а с новой новой функцией Date вы получите постоянную несогласованность. Это означает, что каждая новая итерация функции приведет к новому действительно уникальному ключу.

Уникальный ключ не означает, что он должен быть глобально уникальным, это означает, что он должен быть уникальным в контексте компонента, чтобы он не выполнял бесполезные повторные рендерингы все время. Сначала вы не почувствуете проблему, связанную с новой датой, но почувствуете это, например, если вам нужно вернуться к уже сформированному списку, и React начнет запутываться, потому что не знает, какой компонент изменился, а какой нет, что приводит к утечкам памяти, потому что, как вы уже догадались, в соответствии с вашим ключом Date каждый компонент изменился.

Теперь к моему ответу. Допустим, вы предоставляете список видео YouTube. Используйте идентификатор видео (arqTu9Ay4Ig) в качестве уникального идентификатора. Таким образом, если этот идентификатор не изменится, компонент останется прежним, но если это произойдет, React распознает, что это новое видео, и изменит его соответствующим образом.

Это не должно быть настолько строго, немного более расслабленный вариант - использовать заголовок, как уже указывал Эрез Хохман, или комбинацию атрибутов компонента (заголовок плюс категория), так что вы можете указать React проверить изменились они или нет.

отредактировал некоторые неважные вещи

Ответ 7

Я использую это:

<div key={+new Date() + Math.random()}>