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

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

## はじめに

前回は、チュートリアルのリストとkeyまでをやりました。
が、もう少し気になるところがあるのでもう少し気になるところがあるので、今回も

- [リストとkey](https://ja.reactjs.org/docs/lists-and-keys.html)

までやっています。

修正の内容は、

- ボタンを押した際のメッセージ表示の形式をCSSを使って見た目を変える
- 各ボタンにKeyを付ける

です。
それ以外にTypeScriptの型についても困ったところがあったので、その内容の方が多くなりました。

### リストとKey 続き

[基本的なリストコンポーネント](https://ja.reactjs.org/docs/lists-and-keys.html#basic-list-component) の例を見る限り、
配列もそのまま渡せるんだということで配列を渡すように修正しました。

```ts
const users = [,'Cahal','Edite','Everyone']

const App: React.FC = () => {
  return (
    <div className="App">
      <UserList name={users} />
    </div>
  );
}
```

エラーがでました。

```ts
TypeScript error in src/App.tsx(94,17):
Type '(string | undefined)[]' is not assignable to type 'string'.TS2322

  92 | return (
  93 | <div className="App">
> 94 | <UserList name={users} />
     |           ^
  95 | </div>
  96 | );
  97 | }
```

こちらではstring型だと思っていたものが(string | undefined)というunion型で、型が違うからアサインできないということです。

今まで、すべてstring型で扱っていたものを(string|undefined)に書き換えようと思いましたが、これはなかなか打つのがめんどうです。
今だけならコピペすればいいですが、あとで修正するときにめんどうなので型を作ってしまいたい。

「typescript 型」で調べると出てくるサイトが大体Qiitaなので、この中から型についてまとめてある記事を見つけたので、そちらを参考にします。→[TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)

(string|undefined)自体はunion型で、この場合は文字列かundefinedということです。
じゃあ、これをどうにかすればいいかということで、Type Aliasという、型の名前を付け直す構文で宣言しなおします。

```ts
type StringT = string | undefined;
```

Appに渡すための配列が、見た目はstring型、実際は(string|undefined)のunion型配列ということで、受け側のinterfaceで定義した型ではstringしかないことでエラーになりました。
これをStringTに置き換えてたところで、コンパイルが通りました。

### 文字の大きさ/色

CSSを設定して、メッセージの色や大きさを変更しました。

CSSはもう何年も見てないうえに以前の知識もあやしいので、まずは最初に生成されるデモの中から変更して使います。

```css
.msg {
  font-size: 32px;
  background-color: #282c34;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
}
```

文字の大きさ大きめで中央寄せ、黒バックで少し目立つようにして、それを表示部分に反映させます。
三項演算子…ではなくて、条件演算子でボタンが押されていたら名前を表示、それ以外は何もなしとしています。

配列内の要素にkeyを付けると認識しやすいという説明も読んだので、ボタンに対してKeyを付けるように修正しました。
複数あった場合のためにindexも使ってなるべく一意になるようにしています。

ただ、keyを使って処理するような内容ではないので、書いてあったから付けてみたという程度です。

```ts
render() {
    return (
        <div>
        {
            this.state.name.map((user_name: StringT, index:number) => {
                let key='key_';
                if ( user_name === undefined || user_name === '') {
                  key += 'everyOne' + index;
                    return <Welcome name='everyOne' onClick={() => this.handleClick('everyOne')} key={key} /> 
                } else {
                  key += user_name + index;
                    return <Welcome name={user_name} onClick={() => this.handleClick(user_name)} key={key} /> 
                }
            })
        }
        <div className="msg">{this.state.pushed ? <div>Hello! {this.state.pushed}</div>:''}</div>
        </div>
    )
}
```

最終的なソースです。

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

interface WelcomeProps {
    name: StringT;
    onClick: () => void;
  }
  
  interface WelcomeState {
    name: StringT;
    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: StringT[];
}

interface UserListState {
    name: StringT[];
    pushed: StringT;
//    update: boolean;
}

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

        this.state = {
            name: this.props.name,
            pushed: undefined,
        }
    }

    handleClick(user_name: StringT) {
        //e.preventDefault();
  
        if (this.state.pushed === user_name ) {
            this.setState({
                pushed: undefined,
            })
        } else {
            this.setState({
                pushed: user_name,
//                update: true,
            })
        }
    }

    render() {
        return (
            <div>
            {
                this.state.name.map((user_name: StringT, index:number) => {
                    let key='key_';
                    if ( user_name === undefined || user_name === '') {
                      key += 'everyOne' + index;
                        return <Welcome name='everyOne' onClick={() => this.handleClick('everyOne')} key={key} /> 
                    } else {
                      key += user_name + index;
                        return <Welcome name={user_name} onClick={() => this.handleClick(user_name)} key={key} /> 
                    }
                })
            }
            <div className="msg">{this.state.pushed ? <div>Hello! {this.state.pushed}</div>:''}</div>
            </div>
        )
    }
}

type StringT = string | undefined;

const users = ['','Cahal','Edite','Everyone']

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

export default App;
```

次は、フォームです。