【React+TypeScript】Functional Componentの定義方法

![React+TypeScript.png](https://qiita-image-store.s3.amazonaws.com/0/189588/2c47bc9d-df8a-047f-e86a-03daea808372.png)

React Hooksが来てから、ほとんどFunctional Componentを使うようになりました。

コーディングするときに、デフォルト引数指定やchildren指定はどうやるんだっけと、引っかかることがあったので備忘録として残すことにします。

----

まず、ヘッダ部分とプロパティの定義部分の例です。
プロパティの省略が出来ない`Props`と、省略可能のプロパティがある`PropsOptional`を定義しています。

```ts
import React from "react";
import { storiesOf } from "@storybook/react";

const s = storiesOf("Functional Component", module);

// 省略不可
type Props = {
  x: number;
  y: number;
};

// 省略可能のプロパティあり
type PropsOptional = {
  x?: number;
  y: number;
};
```

コンポーネントの使用例の部分はStorybookを使ってます。
https://qiita.com/otanu/items/dd06d6702f673268c74e


# 引数をpropsで定義

基本パターン。

```tsx
const Ex = (props: Props) => {
  return (
    <div>
      {props.x}:{props.y}
    </div>
  );
};
s.add("Ex", () => <Ex x={1} y={2} />);
```

# 引数をpropsで定義(省略可能)

プロパティを省略可能にするには、`?`をつけて定義すれば、`x?: number | undefined`と定義したことになる。
プロパティを指定しなければ`undefined`が入るので`undefined`が入っても大丈夫なように処理をする必要がある。Object型のプロパティで、それのメソッドを呼び出す場合は、明示的に`undefined`のチェックをしないとエラーになるので少し厄介。

```tsx
type PropsOptional = {
  x?: number;
  y: number;
};

const ExOptional = (props: PropsOptional) => {
  return (
    <div>
      {props.x === undefined ? "なし" : props.x}:{props.y}
    </div>
  );
};

s.add("ExOptional", () => (
  <>
    <ExOptional y={2} />
    <ExOptional x={1} y={2} />
  </>
));
```

`defaultProps`を定義すればデフォルト値の指定が出来る。このやり方だと、プロパティを省略可能ではない`Props`で定義が出来るので`undefined`チェックが要らなくなる。

```tsx
const ExOptional2 = (props: Props) => {
  return (
    <div>
      {props.x}:{props.y}
    </div>
  );
};
ExOptional2.defaultProps = {
  x: 1
};

s.add("ExOptional2", () => <ExOptional2 y={2} />);
```

型の定義だけを見れば、xが省略出来ないように見えるのが少し気持ち悪い気がする。
しかし、コンポーネントを使うときは、VSCodeの補完だと`x?`と表示されるし、処理の中では`undefined`にならないので、
こちらの`Props`を使う方が正解な気がする？

# 引数ハッシュで定義

Functional Componentの定義方法として、props引数の定義ではなく、Hashで定義することも出来る。

```tsx
const ExHash = ({ x, y }: Props) => {
  return (
    <div>
      {x}:{y}
    </div>
  );
};
s.add("ExHash", () => <ExHash x={1} y={2} />);
```

いちいちpropsを指定しなくてもプロパティにアクセス出来るのと、以下の用にデフォルト値を引数の中で指定できる所がメリットだと思う。

```tsx
const ExHashOptional = ({ x = 10, y }: PropsOptional) => {
  return (
    <div>
      {x}:{y}
    </div>
  );
};
s.add("ExHashOptional", () => <ExHashOptional y={2} />);
```

プロパティをまとめて別のコンポーネントに渡したりする場合は`props`引数を使用、簡単なコンポーネントの場合はHash指定という使い分けだろうか。

# childrenプロパティを使用する場合

呼び出し元から子コンポーネントを渡す場合は、プロパティに`children`プロパティを定義する。
childrenを省略可能にする場合は、`children?`と指定すればいい。

```tsx
type PropsWithChild = {
  x?: number;
  y: number;
  children: React.ReactNode;
};

const ExWithChild = ({ x = 10, y, children }: PropsWithChild) => {
  return (
    <div>
      {x}:{y}
      {children}
    </div>
  );
};

s.add("ExWithChild", () => (
  <ExWithChild y={2}>
    <p>子</p>
  </ExWithChild>
));
```

FunctionComponentの型である`React.FC(FunctionComponent)`を使えば、わざわざ`children`を追加した型を定義しなくてもいい。(`PropsOptional`が使える)

```tsx
const ExWithChildFC: React.FC<PropsOptional> = ({ x = 10, y, children }) => {
  return (
    <div>
      {x}:{y + 1}
      {children}
    </div>
  );
};

s.add("ExWithChildFC", () => (
  <>
    <ExWithChildFC y={2} />
    <ExWithChildFC y={2}>
      <p>子</p>
    </ExWithChildFC>
  </>
));
```