LoginSignup
34

More than 5 years have passed since last update.

Reactで動的に行を追加するFormの書き方

Posted at

これは、初心者歓迎!Reactとvte.cxでWebアプリケーションを作成する#2<動作確認〜ソース解説>の説明に使う予定の資料です。

Reactで扱うFormとEntityの管理についての関連で、動的に入力フォームを追加するにはどうすればいいかについて説明します。以下のような入力行を追加するようなやつです。

develop.gif

ソース解説

趣味の入力行は、HobbyFormコンポーネントとして定義され、以下のようにthis.state.feed.entry[0].hobby[]配列の件数分、this.HobbyForm()が呼ばれるようになっています。keyは、Reactでtableを表示する際に必要なものでユニークな番号を与えます。(これはお決まりだと覚えてください)

<ControlLabel>趣味</ControlLabel>
<table className="table">
    <thead>
        <tr>
            <th>タイプ</th>
            <th>名前</th>
        </tr>
    </thead>
    {this.state.feed.entry&&
     this.state.feed.entry[0].hobby&&
     this.state.feed.entry[0].hobby.map((row, key) => this.HobbyForm(key))}
</table>

HobbyFormでは、FormControlで指定しているhobby_typeやhobby_nameに注目してください。
これらは冒頭において、'hobby_type'+keyのように番号のついた名前が定義されます。
これによって、複数行の名前の衝突を防ぐようになっています。<tbody key={key.toString()}>も忘れないように記述してください。これはReactのお決まりだと覚えてください。

    HobbyForm(key:number) {
        const hobby_type = 'hobby_type'+key
        const hobby_name = 'hobby_name'+key
        return(
            <tbody key={key.toString()}>
                <td>
                    <Col sm={8}>              
                        <FormGroup controlId={hobby_type}>
                            <FormControl componentClass="select" placeholder="select" value={this.state[hobby_type]} name={hobby_type} onChange={(e)=>this.handleChange(e)}>
                                <option value="屋内">屋内</option>
                                <option value="屋外">屋外</option>
                                <option value="その他">その他</option>
                            </FormControl>
                        </FormGroup>
                    </Col>
                </td>              
                <td>
                    <Col sm={8}>              
                        <FormGroup controlId={hobby_name}>
                            <FormControl type="text" placeholder="hobby" value={this.state[hobby_name]} name={hobby_name} onChange={(e)=>this.handleChange(e)} />
                        </FormGroup>
                    </Col>
                </td>
            </tbody>
        )
    }

最後に、行が追加されたときに呼ばれるメソッドについてです。

    addRow() {
        this.setState((prevState) => ({
            feed: ((prevState) => { 
                if (!prevState.feed.entry[0].hobby) {
                    prevState.feed.entry[0].hobby = []
                }
                prevState.feed.entry[0].hobby.push({ type: '', name: '' })
                return prevState.feed               
            })(prevState)
        }))         
    }

stateにfeed.entry[0].hobby[]を保持していますが、子要素であるhobby配列に値を追加しても再描画は実行されません。stateの再描画が反応するためにはfeed自体を更新する必要があり、その子要素であるhobby配列に追加した関数の実行結果であるfeedをsetStateに渡す必要があります。

また、上記JSXの例では、stateを使って、value={this.state[hobby_type]}のように値を確保していますが、実はstateの外に定義した統一的なエンティティで管理すれば無駄もなくきれいな形になります。

詳しくは、Reactで扱うFormとEntityの管理についてを参照してください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34