React Hooksの概要

https://reactjs.org/docs/hooks-overview.html

Hooksは、classを記述せずにstateやその他のReactの機能を使えるようにしたもので、今は提案段階です。RFCは[こちら](https://github.com/reactjs/rfcs/pull/68)

現在はReact v16.7.0-alphaで試せます。

Hooks導入の動機はこちら
https://qiita.com/tatane616/items/9d667e32b6f82f9f7a9f

## State Hook
よくあるボタンを押すとカウントが増えていくサンプルです。

```js
import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
```

この中の`useState`がHookです。
関数コンポーネントの中でもローカルステートを利用できるようにしたもので、現在のstateとstateを更新する関数の2つを返します。
古いstateと新しいstateを一緒にマージしないと言う点をのぞいて、classの`this.state`に似ています。

`useState`の引数の`0`は初期値で、`this.state`とは違いオブジェクトである必要はありません。

State　Hookは1つのコンポーネントで複数利用でき、その場合は次のように記述します。

```js
function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}
```

Hooksは、stateやライフサイクルメソッドを関数コンポーネントから利用する機能で、class内では機能しません。

## Effect Hook
`useEffect`は関数コンポーネントで副作用を実装できるようにしたHooksで、データをフェッチしてきたり、DOMをいじったりなど今までclass内の`componentDidMount`,`componentDidUpdate`,`componentWillUnmount`でやってきたロジックを単一のAPIに統合すると言うもの。

DOMのアップデート後にtitleを変えるサンプル

```js
import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
```

Effectsはコンポーネント内で宣言され、propsやstateにアクセスが可能。デフォルトでは、初回を含むレンダリングのたびに実行されます。

Effectsでは、任意で関数を返すことによってどのようにクリーンアップするかを指定することもできます。
次のコードは、友人のオンラインステータスをsubscribeし、のちにunsubscribeするサンプルです。

```js
import { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
```

この例では、コンポーネントがアンマウントされる時や再レンダリングでEffectが実行される時に`ChatAPI`をunsubscribeします。

`useState`と同様に、Effectも1つのコンポーネントに複数宣言することができます。

```js
function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...
```

## Hooksのルール
HooksはJSの関数ですが、2つのルールがあります。

- トップレベルでのみ呼び出す。ループやifなどの条件文、ネストされた関数の中で呼び出してはいけない。
- Reactの関数コンポーネントでのみ呼び出す。普通のJSの関数で呼び出してはいけない。

## Custom Hooks
ステートを持ったロジックをコンポーネント間で再利用したい時は、今まではHOCやrender propsで解決していたと思いますが、Custom Hooksを使うことでツリーに余計なコンポーネントを追加せずに実装することができます。

先ほどのオンラインステータスのコードを例に見ていきましょう。
これらのロジックを`useFriendStatus`と言うHookに抽出します。

```js
import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
```

`friendID`を引数にとって、友人がオンライン状態かどうかを返します。

このCustom Hooksは以下のように利用することができます。

```js
function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
```

```js
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}
```

上記のコンポーネントのstateはそれぞれ独立しています。Hooksはステートフルなロジックを再利用するもので、それ自体がstateを持つわけではありません。

関数の名前を`use`から始めることで、それを他のコンポーネントから呼んだ際にCustom Hookと言うことができます。
`useSomething`と言う名前は、リンタープラグインなどでHooksを利用したコードでのバグを見つけることなどに役立ちます。

## Other Hooks
他にも`useContext`や`useReducer`といった便利なHooksがあります。

`useContext`は、ネストすることなくReact Contextを利用することができます。

```js
function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}
```

`useReducer`は複雑なコンポーネントでのローカルステートをreducerを使って管理できるようになります。

```js
function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...
```

[Hooks API reference](https://reactjs.org/docs/hooks-reference.html)