ReactでHelloWorldしてから、ちょっとずつ足していく #2

[ReactでHelloWorldしてから、ちょっとずつ足していく #1](https://qiita.com/sato_c/items/e8c41eee6cbcf45355ab)

## はじめに

前回は、チュートリアルのstateとライフサイクルまでをやりました。  
元のファイルをいろいろと修正して、Welcomeタグで指定された名前を表示するときに「Hello! <名前>」という形式で表示する処理までを自前で考えつつ実装しています。

今回は、

- [イベント処理](https://ja.reactjs.org/docs/handling-events.html)
- [条件付きレンダー](https://ja.reactjs.org/docs/conditional-rendering.html)
- [リストとkey](https://ja.reactjs.org/docs/lists-and-keys.html)

までやりました。  

修正の内容は、

- 表示をボタンにする(buttonタグ)
- 押されたら、メッセージ表示と非表示を切り替える

といったところです。

### イベント処理/条件付きレンダー

イベント処理は、ボタンを設置して押されたときの状態を切り替える内容でした。  
条件付きレンダーは、フラグに応じて表示を切り替える内容でした。

ログインフラグの状況によって、ログイン/ログアウトを切り替えるということだったので、名前の要素を渡すとそのボタンを人数分表示して、押されたらHello!のメッセージを表示/非表示するようにしてみます。

現状のソースです。

```
import React from 'react';
// import logo from './logo.svg';
import './App.css';

interface WelcomeProps {
    name: string;
    onClick: () => void;
  }

  interface WelcomeState {
    name: string;
    onClick: () => void;
  }

  class Welcome extends React.Component<WelcomeProps, WelcomeState> {
    constructor(props: WelcomeProps) {
      super(props);

      this.state = {
          name: props.name,
          onClick: props.onClick,
        }
    }

    render() {
      return (
            <button onClick={this.state.onClick} >
          {this.state.name}
          </button>
      );
    }
  }

interface UserListProps {
    name: string;
}

interface UserListState {
    name: string[];
    pushed: string;
}

class UserList extends React.Component<UserListProps, UserListState> {
    constructor(props: UserListProps) {
        super(props)

        this.state = {
            name: this.props.name.split(','),
            pushed: '',
        }
    }

    handleClick(user_name: string) {
        //e.preventDefault();

        if (this.state.pushed === user_name ) {
            this.setState({
                pushed: '',
            })
        } else {
            this.setState({
                pushed: user_name,
            })
        }
    }

    render() {
        return (
            <p>
            {
                this.state.name.map((user_name) => {
                    if ( user_name === '' ) {
                        return <Welcome name='everyOne' onClick={() => this.handleClick('everyOne')} />
                    } else {
                        return <Welcome name={user_name} onClick={() => this.handleClick(user_name)} />
                    }
                })
            }
            <h1>{this.state.pushed ? <div>Hello! {this.state.pushed}</div>:''}</h1>
            </p>
        )
    }
}

const App: React.FC = () => {
  return (
    <div className="App">
      <UserList name=",Cahal,Edite,Everyone" />
    </div>
  );
}

export default App;
```

### 管理関数の追加

Welcomeだけで終わらせてもいいのですが、ここはちょっとだけそれっぽく見えるようにしたいと思って親要素UserListを作りました。

UserListは、メンバーの状態管理を行います。  
1つのプロパティで複数の内容を送る方法がわからなかったので、このようにしています。本当は配列が渡せればありがたいですが、それは無理かと思うのでこの方法にしました。

ボタン処理の実体もこちらに実装して、Welcomeタグの呼び出しで関数をパラメータとして渡しています。  
Welcome側では受け取った名前と関数を保存してレンダリングします。

ボタン処理には、引数をつけてどの名前なのかを指定するつもりでしたが、Welcome側で引数を追加しようとしてもうまく行かず、呼び出し側で引数を付けた関数を指定しています。

```
 // 実体
 handleClick(user_name: string) {

 // 要素を渡す際
 return <Welcome name={user_name} onClick={() => this.handleClick(user_name)} />
```

```
// 受け取り側
interface WelcomeProps {
    name: string;
    onClick: () => void;
  }
```

受け取り側に引数をつけないとダメなのかと思っていたのですが、受け取り側はそのままにして渡す側は引数付きにしたところ、うまくいきました。  
渡された関数がonClickにそのまま登録されるという理解ですが、このあたりはのちほど詳細を調べます。(関数オブジェクトを渡してるから、ってことじゃないかという推測)

### いっぺんにいれる

```
render() {
    return (
        <p>
        {
            this.state.name.map((user_name) => {
                if ( user_name === '' ) {
                    return <Welcome name='everyOne' onClick={() => this.handleClick('everyOne')} />
                } else {
                    return <Welcome name={user_name} onClick={() => this.handleClick(user_name)} />
                }
            })
        }
        <h1>{this.state.pushed ? <div>Hello! {this.state.pushed}</div>:''}</h1>
        </p>
    )
}
```

要素をレンダリングする際に今回のチュートリアルの要素を混ぜて利用しています。

this.state.nameは配列なので、これをmapで1つずつ取りだしてWelcomeタグに渡します。このとき、空文字列を許容しているので、空文字列だった場合は「everyOne」を指定します。

次に、押されたメンバーの名前が保存されていたら要素を返し、保存されていなければ空文字列を返します。  
同じ名前のメンバーボタンが押されるたびにthis.state.pushedには名前と空文字列が交互に入るので、ここで条件演算子(三項演算子)を使っています。

### renderで指定できる要素は1つだけ

render関数でパラグラフ(pタグ)を使っていますが、この外にh1タグでメッセージを出そうとしたら以下のエラーがでました。

```
Parsing error: JSX expressions must have one parent element
```

1つの親要素しか使えないということで、言語仕様を理解してなかったためのエラーでした。  

## まとめ

今回は、読んだ内容とtic-tac-toeでもやったことを思い出して、要素の分解を中心に書き換えしました。  
はまった点としては、

- map処理を入れ込む
- 引数指定したhandleClickを渡したときの宣言
- renderで指定できる要素は1つだけ

です。

次は、リストとkeyのkeyの設定などをもう少し考えつつ、続きをやっていきます。