React Hooksの解説

# ReactHooks

Hooks は既にあなたが知っている React のコンセプトを置き換えるものではありません。むしろ、フックはあなたが既に知っている props、state、コンテクスト、ref、ライフサイクルといったコンセプトに対してより直接的な API を提供するものです。後でお見せするように、フックによって、これらを組み合わせるパワフルな手段も得ることができます。

React からクラスを削除する予定はありません。

公式抜粋

## hooksのルール 

- フックは関数のトップレベルのみで呼び出してください。ループや条件分岐やネストした関数の中でフックを呼び出さないでください。
- フックは React の関数コンポーネントの内部のみで呼び出してください。通常の JavaScript 関数内では呼び出さないでください
    - カスタムフックの場合は例外

公式抜粋

## 個人的な意見

- ページ全体の制御をする場合は従来のクラスコンポーネントを使用するべき
    - 込み入った事をしようとするとちょこちょこハマる
- 便利ではあるが多用するとわけわからない事になるので、SFC( Stateless Functional Component )とコンポーネントの、間に位置する存在
- アニメーションとかはこれのおかげで多少強くなった気がする。

## useState

```javascript
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
```

## useEffect 

類似hooks

- useLayoutEffect (コレに関してはほぼほぼ同じ)
- useMemo 
- useCallback

ComponentDidMount

```javascript
useEffect(() => {
  console.log('component did mount');
}, []);
```

ComponentWillUnMount

```javascript
useEffect(() => {
  return  () => {
    console.log('component will unmount');
  };
}, []);
```

ノンブロッキングな命令であるため正確にはDidMountやWillMountとは挙動が異なる

### useEffect系の解説

- 第一引数に実行したい関数を渡す
    - `return`に記述したものは、コンポーネントが破棄される際に呼ばれる
        - コネクションを明示的に切る必要のあるソケットだけの処理を外に記述できるメリットがある
- 第二引数に変更がかかったさいに変更したい関数を渡す

### useEffectとuseLayoutEffectの違い

上記で解説した通りノンブロッキングな命令に対し
`useLayoutEffect`は同期的に実行されるため、アニメーションなどに向いている（らしい）

### useMemoとは

```javascript
const memoHoge = useMemo(() => {
  return props.hoge
}[props.hoge]);
```

`props.hoge`の値が変わると、コールバックが使用され帰り値が変更される。

vueのcomputedのようなもの

### useCallbackとは

```javascript
const memorizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);
```

中の値のcallbackを返却します。

```javascript
const memorizedCallback = useMemo(() => {
  return () => {
    doSomething(a, b);
  }
}, [a, b]);
```

と同じようなもの

#### ハマりどころ

- useStateと一緒に使用する際下記のように記述しないと、依存ステータスが更新されない

```javascript
  const [count, setCount] = React.useState(0);

  const sampleCallback = React.useCallback(
    (event) => {
      console.log(event.target);
      setCount(count + 1);
    },
    [count] // ここがないとカウントが1以上進まなくなる
  );
```

## useRef

ref要素を取得するための箱を提供する。

愚直にグローバルに書いても問題無いとは思うが、こちらのほうがスマートな印象がある。

```jsx
const TextInputWithFocusButton = () => {
  const inputEl = useRef(null);
  const onButtonClick = useCallback((event) => {
    inputEl.current.focus(); // currentでrefの参照先が取れる
  }, [inputEl]);
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>テキストを入力</button>
    </>
  );
}
```

## useReducer

- useStateの代替品
    - 普通にReact-Reduxを簡単にしただけのもの
- Stateが氾濫した際に有用

```tsx
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  // const [state, dispatch] = useReducer(reducer, initialState, initialAction)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}
```


## useContext

- ここでStoreが簡単に作れるようになる

```jsx
const value = useContext(MyContext);
```

- React.createContext からの戻り値を受け取り、そのコンテクストの現在値をを返却する。
    - コンテクストの現在値は、ツリー内でこのフックを呼んだコンポーネントの直近にある <MyContext.Provider> の value の値によって決定されます。


### Storeとかも簡単に作れるようになる

```tsx
import ReactDOM from 'react-dom';
import React, { useReducer, useContext } from 'react';

// createContextを使用する
const context = React.createContext();

interface IState {
  count: number;
  user: {
    name: string;
  };
}

const initialState: IState = {
  count: 0,
  user: { name: 'araki' }
};

const reducer = (state: IState, action: any) => {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1
      };
    default:
      return state;
  }
};

// contextへ stateとdispatchをバインドする
const Provider = (props: { children: any }) => {
  const { children } = props;
  const [state, dispatch] = useReducer(reducer, initialState);

  return <context.Provider value={{ state, dispatch }}>{children}</context.Provider>;
};
 
const DispatchSample = () => {
  const { state, dispatch } = useContext(context);

  return (
    <div>
    <div>{state.count}</div>
      <button onClick={() => dispatch({ type: 'INCLEMENT' })}>
        increment
      </button>
    </div>
  )
}

const App = () => {
  return (
    <Provider>
      <DispatchSample />
    </Provider>
  );
};

ReactDOM.render(<App />, document.querySelector('#app'));
```

`useContext` は非常に強いなと感じましたが
`ClassComponent` で従来通りのStoreを使用したい際は、 `Connect` の `HOC` を自作すれば簡単に実現可能になります。

## 余談

### 面白そうなHooks製ライブラリ

#### react-vim

[rhysd/react-vim-wasm： Vim editor empowers your React web application](https://github.com/rhysd/react-vim-wasm)
[Example of react-vim-wasm](https://rhysd.github.io/react-vim-wasm/)

- Web上でVIMが使用できるらしい

#### react-use

[streamich/react-use： React Hooks — 👍](https://github.com/streamich/react-use)

- スター数が結構多いHookライブラリ
    - かゆいところに手が届く感じの機能が多い

- センサー系のHooks
    - ジオロケーション            `useGeolocation`
    - モーションセンサー          `useMotion`
    - ネットワークの状態          `useNetwork`
    - デバイスの画面の向きの状態    `useOrientation`
- UI系のHooks
    - オーディオの再生とコントロール `useAudio`
    - ビデオのコントロール          `useVideo`
    - ビデオのフルスクリーン        `useFullscreen`
    - UI用の待機系命令             `useWait`
    - 音声入力                    `useSpeech`
- その他
    - 非同期依存注入   `useAsync`
    - 通信のリトライ   `useRetry`
    - クリップボードへコピー `useCopyToClipboard`
    - ユーザーがページをリロードや閉じる際に警告を促す `useBeforeUnload`
    - bodyのスクロールをロックする（Modalとかのやつ） `useLockBodyScroll`
    - sessionStorageの管理 `useSessionStorage`

#### the-platform

[palmerhq/the-platform： Web. Components. 😂](https://github.com/palmerhq/the-platform)

- インタラクティブなデバイスの情報取得系命令が詰まったHooks
    - 回転度？とか色々取れるみたい `useDeviceMotion`
    - ジャイロスコープ？の情報取得 `useDeviceOrientation`
    - スクリプトの依存注入 `useScript` ※自分がパフォーアンスチューニングでやってたやつ
    - CSSの依存注入 `useStylesheet` 上記のCSS版