1. ホーム
  2. reactjs

[解決済み] Reactの要素にユニークなキーを作成するには?

2022-08-25 18:18:17

質問

リストを作って保存できるReactアプリを作っているのですが、Reactは要素がユニークキープロップを持っていないという警告を出しています(要素 List/ListForm)。ユーザが作成した要素に対して、どのようにユニークなキーpropを作成すればよいのでしょうか?以下は、私の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>

どのように解決するのですか?

を作成する方法はたくさんあります。 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 のように、2番目のパラメータ 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氏が指摘されているように、これは必ずしも理想的ではありません。

では、どのような解決策があるのでしょうか?

多くの

  • ユニークなキー/ID/数値/文字列を生成する関数を作成し、それを利用することができます。
  • のような既存の npm パッケージを利用することができます。 uuid , ユニコード など
  • のような乱数を生成することもできます。 new Date().getTime(); のような乱数を生成し、その一意性を保証するために反復処理中のアイテムから何かを前置することもできます。
  • 最後に、データベースから取得したユニークIDを使用することをお勧めします(If you get it.

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>
    )
});