React hooksによる関数コンポーネントの作成

クラスコンポーネントが書ける人向けの関数コンポーネントの書き方まとめです。

# 基本

JSXを戻り値として返す関数を作成する。その関数がコンポーネントになる。

``` tsx
const SampleComponent = () => {
  return (<div>Hello!!</div>);
}

export default SampleComponent;
```

## プロパティ

プロパティは、引数からオブジェクトとして渡ってくる。

``` tsx
const SampleComponent = ({name}) => {
  return (<div>Hello!! {name}!!</div>);
}

export default SampleComponent;
```

## State

`State`は`useState`で利用できる。引数は初期値。

戻り値として配列が返る。配列の一つ目にStateの値が、二つ目にStateを設定するための関数が入っている。

``` tsx
const SampleComponent = () => {
  const [name, setName] = useState('hoge');

  setName('sfjwr');

  return (<div>Hello!! {name}!!</div>);
}

export default SampleComponent;
```


# 関数(コンポーネント)が呼ばれるタイミング

DOMの描画が必要なタイミングにて呼び出される。

## 呼ばれるタイミングの具体例
- 初期描画時
- 親から渡されているプロパティが更新された時
- 自身で持っているStateが更新された時
- 参照しているReduxの値が更新された時

など。
基本的に、コンポーネントの入力となっている値が変更された時に呼び出される。

## コンポーネント階層の部分的な更新

とあるコンポーネントが自身のStateを変更した時、自分自身の関数は呼びだされるが、親の関数は呼びだされない。変更のあったコンポーネントより下の階層の関数は呼び出される。

## 親が呼ばれた時、自分を呼んで欲しくない
プロパティについて何も値が変わっていないなら、パフォーマンスの都合で呼んで欲しくない。そのような場合は`React.memo`を利用する。

``` tsx
const SampleComponent = React.memo(({message}) => {
  return (<div>Hello!! {message}</div>);
});

export default SampleComponent;
```

`React.memo`で関数をラップすることで、プロパティの変化を監視し、プロパティに変更がなければ関数の呼び出しが省略される。

上記例ではプロパティ`message`が変更されない限り、関数は呼び出されない。

### `React.memo`したが、プロパティにコールバック関数を受け取っている時

コールバック関数をプロパティに受け取るコンポーネントに対して、ローカル関数をそのまま渡していると、そのプロパティは毎回変化することになってしまい、`React.memo`した意味がなくなる。

``` tsx
const App = () => {
  const click = () => {
    console.log('Hello');
  }

  return (<SampleComponent onClick={click} />);
}

export default SampleComponent;
```

上記例では、コンポーネント`App`から`SampleComponent`を利用しており、`onClick`にローカルの関数を渡している。

しかし、渡している関数`click`は`App`の呼び出しの度に生成されているため、`onClick`には毎回異なる`click`が渡されている。そのため、`App`の呼び出しの都度`SampleComponent`も呼び出されてしまう。

これを回避するためには、関数`click`を`useCallback`でラップする

``` tsx
const App = () => {
  const click = useCallback(() => {
    console.log('Hello');
  });

  return (<SampleComponent onClick={click} />);
}

export default SampleComponent;
```

`useCallback`でラップすることによって、初回呼び出し時に作成された関数が保存され、以降はそれが`useCallback`から戻されるようになる。

２回目以降も関数は作成されているが、`useCallback`が無視して捨ててくれる。

ただし、もしそのコールバック関数から`State`などを参照している場合は注意が必要である。

なぜなら、

``` tsx
const App = () => {
  const [message, setMessage] = useState('Hello');

  const click = useCallback(() => {
    console.log(message);
  });

  return (<SampleComponent onClick={click} />);
}

export default SampleComponent;
```

このような例の場合、`useCallback`初回呼び出し時には`message`には`'Hello'`が入っている。

しかし以降のタイミングで`setMessage`されると、`message`の内容は変化することになる。

が、`useCallback`は初回の関数を保持しており、初回の関数は初回の`message`を参照しているため、`console.log`される内容が変化しなくなってしまう。

そのため、`message`が変化した時には`click`関数を作り直す必要がある。

これは`useCallback`の第二引数で可能であり、

``` tsx
const App = () => {
  const [message, setMessage] = useState('Hello');

  const click = useCallback(() => {
    console.log(message);
  }, [message]);

  return (<SampleComponent onClick={click} />);
}

export default SampleComponent;
```

としておけば、`useCallback`が`message`の変更を監視し、変更があった時には保持している古い関数を破棄し、引数から渡ってきている新しい関数で置き換えてくれる。

こうすることで`message`の変化時のみ`click`関数が入れ替えられるようになり、また`SampleComponent`の`onClick`プロパティもその時のみ変化するようになり、うまく動作する。

# `useEffect`について

ネットワークからのデータ取得など、タイミングをずらして処理しなくてはならないものを記述する。

`useCallback`と同様に、変数の変化をトリガーに実行できる。

``` tsx
const SampleComponent = ({userId}) => {
  const [showName, setShowName] = useState('');

  useEffect(() => {
    const load = async () => {
      const name = await getNameFromUserId(userId);
      setShowName(name);
    }

    load();
  }, [userId]);

  return (<div>Hello!! {showName}</div>);
}

export default SampleComponent;
```

このようにすることで、プロパティから渡ってくる`userId`に応じてネットワークからユーザ名を取得して画面に反映させる、というようなことができる。

# その他

- コンポーネントが再描画されても更新されるのは仮想DOMなので、本当に再描画されるわけではない(はず)
  - 変化のあったところのみ本当に描画される
- パフォーマンスに問題がなければ、無理に`React.memo`しなくていいかも