react-useで使える便利なReact Hooksを紹介します

# react-use とは

様々な React の Hooks を定義しているライブラリです。
用意されている Hooks の数が多く、バリエーションも豊富な為、これから作ろうと思っていた Hooks が react-use に既にあった、という事が起こり得ます。
Hooks の数が膨大なので、簡単な一つの Hooks を使う為だけにこのライブラリを導入するべきかは要検討ですが、ある程度の規模のプロダクトであれば色々なところで利用する局面はありそうです。

https://github.com/streamich/react-use

## インストール
```
## yarnの場合
yarn add react-use

## npmの場合
npm i react-use
```

## 用意されている Hooks 一覧
数が多いのと、日々更新されているので、全項目は網羅出来ておりません。
全ての Hooks を確認したい場合は、[公式の GitHub ](https://github.com/streamich/react-use "GitHub") をご参照ください。

## 状態感知系
| Hooks | 説明 |
| -- | -- |
| useBattery | デバイスのバッテリー状況を取得する |
| useGeolocation | ユーザーの位置情報を取得する |
| useHover と useHoverDirty | 要素がマウスでホバーされているかどうかを追跡する |
| useHash | URLハッシュの取得や、ハッシュの設定が行える |
| useIdle | ページ上のユーザーがアイドル状態であるかどうかを追跡する |
| useIntersection | ターゲット要素と祖先要素、または最上位ドキュメントのビューポートとの交差の変化を追跡する |
| useKey | キーボードのキーが使用されたときにコールバックを実行する |
| useLocation | ブラウザロケーションを取得する |
| useLongPress | クリックを長押しした際にコールバックを発生させる |
| useMedia | メディアクエリで状態を切り替える |
| useMediaDevices | 接続されたハードウェアデバイスを追跡する |
| useMotion | デバイスの加速度センサーを使用してモーションを追跡する |
| useMouse と useMouseHovered | マウスの位置が変化したときに再レンダリングする |
| useMouseWheel | ウィンドウでスクロールされたマウスの deltaY を取得する |
| useNetworkState | ブラウザのネットワーク接続の状態を追跡する |
| useOrientation | ユーザーのデバイスの画面の向きを追跡する |
| usePageLeave | マウスがページから離れたときにコールバックを発生させる |
| useScratch | マウスの「スクラブ」(または「スクラッチ」) の状態を追跡する |
| useScroll | DOM 要素のスクロール位置が変更されたときに再レンダリングする |
| useScrolling | ユーザーがスクロールしているかどうかを追跡する |
| useStartTyping | ユーザーが入力を開始したときにコールバックを発生させる|
| useWindowScroll | ウィンドウのスクロール時に再レンダリングする |
| useWindowSize | ブラウザウィンドウのサイズを追跡する |
| useMeasure | Resize Observer APIを使用して HTML 要素の寸法を追跡する |
| createBreakpoint | ブレークポイントを設定する |
| useScrollbarWidth | 現在のブラウザのスクロールバーの幅を返す|
| usePinchZoom | ポインター、タッチイベントの変化を追跡し、ピンチ差の値を検出して、ユーザーがズームインまたはズームアウトしているかどうかを通知する |

## UI系
| Hooks | 説明 |
| -- | -- |
| useAudio | audio 要素を作成し、再生コントロールを表示する |
| useClickAway | ユーザーがターゲット要素の外側をクリックしたときにコールバックする |
| useCss | CSS を動的に変更する|
| useDrop と useDropArea | ファイル、リンクのドロップ、およびコピーと貼り付けでトリガーされる|
| useFullscreen | 要素をフルスクリーンで表示する |
| useSlider | HTML 要素上でスライド動作を提供する |
| useSpeech | 与えられた文字列を話す人間の声を合成する |
| useVibrate | Vibration APIを使用してデバイスのバイブレーションを発生させる |
| useVideo | video 要素を作成し、再生コントロールを表示する |


## アニメーション系
| Hooks | 説明 |
| -- | -- |
| useRaf | requestAnimationFrameごとにコンポーネントを強制的に再レンダリングし、経過時間のパーセンテージを返す |
| useInterval | 数値のカウントアップなど、間隔をセットして、状態の変化をアニメーションさせる |
| useHarmonicIntervalFn | useIntervalと似ているが、こちらはすべてのエフェクトを同じ遅延で同時にトリガーする |
| useSpring | スプリングの動きに合わせて一つの数値を時間と共に更新する |
| useTimeout | 指定されたミリ秒後にコンポーネントを再レンダリングする|
| useTimeoutFn | 指定されたミリ秒後に指定された関数を呼び出す |
| useTween | 0 と 1 の間の数値をトゥイーンする |
| useUpdate | 呼び出されたときにコンポーネントを強制的に再レン​​ダリングする関数を返す |


## 副作用系
| Hooks | 説明 |
| -- | -- |
| useAsync | 非同期関数をコンポーネント内で同期的に扱うことを可能にする |
| useBeforeUnload | ユーザーがページを再読み込みまたは閉じようとしたときにブラウザのアラートを表示する |
| useCookie | Cookie の現在の値を返したり、更新、削除を行う |
| useCopyToClipboard | テキストをユーザーのクリップボードにコピーする |
| useDebounce | 連続する処理が指定した時間内に何度発生しても、最後の一度だけ実行する|
| useError | エラーディスパッチャーを返す |
| useFavicon | ページのファビコンを設定する |
| useLocalStorage | LocalStorageにて、単一のキーでデータを管理する |
| useLockBodyScroll | body 要素のスクロールをロックする|
| useRafLoop | 親コンポーネントを再描画することなく、RAFのループ内で指定された関数を呼び出す。ループはコンポーネントのアンマウント時に自動的に停止する |
| useSessionStorage | SessionStorageにて、単一のキーでデータを管理する |
| useThrottle と useThrottleFn | 指定したミリ秒が経過するまで、関数の呼び出しを遅延させる |
| useTitle | ページのタイトルを設定する |
| usePermission | ブラウザAPIのパーミッション状況を問い合わせる |


## ライフサイクル系
| Hooks | 説明 |
| -- | -- |
| useEffectOnce | 一度だけ実行される useEffect |
| useEvent | イベントハンドラをサブスクライブする  |
| useLifecycles | コンポーネントがマウントされたときとアンマウントされたときに、mount と unmount のコールバックを呼び出す |
| useMountedState | コンポーネントがマウントされているかどうかを返す |
| usePromise | Promise をラップするためのヘルパー関数を返す。この関数でラップされた Promise は、コンポーネントがマウントされたときのみ解決される |
| useLogger | コンポーネントがライフサイクルを通じて遷移する際に、パラメータをコンソールログに記録する |
| useMount | コンポーネントがマウントされた後に関数を呼び出す |
| useUnmount | コンポーネントがアンマウントされるときに関数を呼び出す |
| useUpdateEffect | 最初の呼び出し (マウント時など) を無視する |

## state系
| Hooks | 説明 |
| -- | -- |
| createMemo | 指定した関数をメモ化する。元の関数と同じ引数を受け取り、同じ結果を返す |
| createReducerContext | React の useReducer と同じ振る舞いをするが、 Context として全てのコンポーネントで共有される|
| useDefault | 状態が null または未定義の場合にデフォルト値を返す |
| useLatest | State の最新の状態を返す |
| usePrevious | State の前の値を返す |
| usePreviousDistinct | usePrevious と同じだが、値が実際に変更された場合にのみ更新される |
| useSetState | クラスコンポーネントの this.setState の動作と同様に動作する setState メソッドを作成する |
| useToggle | 状態の Boolean を反転させる |
| useCounter | 数値カウントを追跡する |
| useList | 配列の値を追跡し、それを変更するためのメソッドを提供する |
| useMap | オブジェクトの値を追跡し、それを変更するためのメソッドを提供する |
| useStateValidator | 指定された State が変化するたびに、バリデータ関数が呼び出される |
| useRendersCount | 最初のレンダリングを含む、コンポーネントのレンダリング回数をトラッキングする |
| useGlobalState | グローバルに共有される State を作成する|

## 使用例紹介
数が多いので全ては紹介できませんが、個人的に面白いと思った Hooks をピックアップします。

### useHover
要素が Hover されているかどうかを追跡します。
引数に指定した React 要素が Hovered のターゲットとなり、Hover されていればコンポーネントを表示する、というような処理が出来ます。
```tsx
import { useHover } from "react-use";

export default function App() {
  const hoverTargetElement = (hovered) => <div>{hovered ? "Hovered" : "Hover me!"}</div>;
  const [hoverable, hovered] = useHover(hoverTargetElement);

  return (
    <div>
      {hoverable}
      {hovered && <div>Hoverされました</div>}
    </div>
  );
}
```

### useKey
キーボードの入力を受け取って、指定した処理を実行します。
第一引数に`KeyboardEvent.key`プロパティの値、第2引数にイベントハンドラを渡します。
例えば、上矢印入力でカウントアップ、下矢印入力でカウントダウンを行う場合は以下のようになります。
```tsx
import { useKey } from "react-use";

export default function App() {
  const [count, set] = useState(0);
  const increment = () => set((count) => ++count);
  const decrement = () => set((count) => --count);
  useKey("ArrowUp", increment);
  useKey("ArrowDown", decrement);

  return <div>Press arrow up: {count}</div>;
}
```

### useMedia
メディアクエリで状態の出し分けを行えます。
PC レイアウトと SP レイアウトを、指定したブレークポイントを起点に切り替える事が出来ます。
```tsx
import { useMedia } from 'react-use'

export default function App() {
  const isWide = useMedia('(min-width: 480px)')
  return (
    <>
      <div>{isWide ? (<div>PC Contents</div>) : (<div>SP contents</div>)}</div>
    </>
  )
}
```

### useToggle
状態の Boolean を反転させる Hooks です。
Toggle 系の処理は簡単なのですぐに実装出来ますが、利用箇所が多くなればなる程この Hooks の便利さに気付けます。
```tsx
import { useToggle } from 'react-use'

export default function App() {
  const [on, toggle] = useToggle(false)
  return (
    <>
      <button onClick={toggle}>{on ? 'active' : 'inactive'}</button>
    </>
  )
}
```

### useClickAway
ターゲット要素の範囲外がクリックされた際にコールバックする Hooks です。
例えば、モーダルの実装で範囲外がクリックされた場合にモーダルを閉じる、というような挙動を実装するのに便利です。
以下は、Button クリックで要素の出し分けを行い、要素の外側がクリックされた場合にも非表示処理を行なっています。
```tsx
import { useClickAway, useToggle } from 'react-use'

export default function App() {
  const [on, toggle] = useToggle(false)
  const ref = useRef(null)
  useClickAway(ref, () => {
    toggle()
  })
  return (
    <>
      {on && (
        <div
          ref={ref}
          style={{
            width: 200,
            height: 200,
            background: 'red',
          }}
        />
      )}
      <button onClick={toggle}>{on ? 'close' : 'open'}</button>
    </>
  )
}
```

### usePageLeave
マウスが画面外に飛び出した際に発火する Hooks です。
面白かったので、どんな時に使えるのかを考えてみたのですが、何も浮かびませんでした・・・。
```tsx
import { usePageLeave } from 'react-use'

export default function App() {
    usePageLeave(() => alert('Stay here'))

    return null;
}
```

### useSessionStorage
セッションストレージにデータを保存します。
第一引数にストレージのキー、第二引数に初期値、第三引数は JSON 形式にシリアライズするかどうかを Boolean で渡します。
`JSON.stringify` でシリアライズ化して、 `JSON.parse` でデシリアライズ化するという一連の流れを全部やってくれます。
ちなみに`useLocalStorage` も用意されていて、そちらではシリアライズをカスタマイズできるみたいです。
```tsx
import { useSessionStorage } from 'react-use'

export default function App() {
  const [sessionStorage, setSessionStorage] = useSessionStorage('my-key', 'foo')
  const [value, setValue] = useState('')

  const handleSubmit = () => {
    setSessionStorage(value)
  }

  const handleChange = (e) => {
    setValue(e.target.value)
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
      <div>storage: {sessionStorage}</div>
    </form>
  )
}
```

### useCookie
Cookie の値を読んだり、更新したり、削除したり。
```tsx
import { useCookie } from 'react-use'

export default function App() {
  const [cookieValue, updateCookie, deleteCookie] = useCookie('cookie')

  useEffect(() => {
    deleteCookie()
  }, [])

  const updateCookieHandler = () => {
    updateCookie(Math.random().toString(32).substring(2))
  }

  return (
    <div>
      <p>cookie value: {cookieValue}</p>
      <button onClick={updateCookieHandler}>Update Cookie</button>
      <br />
      <button onClick={deleteCookie}>Delete Cookie</button>
    </div>
  )
}
```

### useLockBodyScroll
body のスクロールをロックする Hooks です。
モーダルやオーバーレイコンポーネントに使えます。
```tsx
import { useLockBodyScroll, useToggle } from 'react-use'

export default function App() {
  const [locked, toggleLocked] = useToggle(false)

  useLockBodyScroll(locked)

  return (
    <div style={{
          display: 'flex'
        }}>
      <div
        style={{
          width: 200,
          height: '150vh',
          background: 'red',
        }}
      />
      <button onClick={() => toggleLocked()}>{locked ? 'Unlock' : 'Lock'}</button>
    </div>
  )
}
```

### useDefault
状態が null または undefined の場合に使用する、デフォルト値を保持する事ができます。
第一引数にデフォルト値、第二引数に初期値とし、状態が null または undefined になった場合にデフォルト値が呼ばれます。
```tsx
import { useDefault } from 'react-use'

export default function App() {
  const initialUser = { name: 'initial' }
  const defaultUser = { name: 'default' }
  const [user, setUser] = useDefault(defaultUser, initialUser)

  return (
    <div>
      <div>User: {user.name}</div>
      <input onChange={(e) => setUser({ name: e.target.value })} />
      <button onClick={() => setUser(null)}>set to null</button>
    </div>
  )
}
```

### useScrolling
指定した `Ref` オブジェクトがスクロール状態であるかどうかを追跡します。
スクロール中かそうでないかで処理を出し分ける事が可能です。
```tsx
import { useScrolling } from 'react-use'

export default function App() {
  const scrollRef = useRef(null)
  const scrolling = useScrolling(scrollRef)

  return (
    <div
      ref={scrollRef}
      style={{
        display: 'flex',
        height: '100vh',
        overflow: 'scroll',
      }}
    >
      <div
        style={{
          width: 200,
          height: '150vh',
          background: 'red',
        }}
      />
      <div
        style={{
          position: 'fixed',
        }}
      >
        {<div>{scrolling ? 'Scrolling' : 'Not scrolling'}</div>}
      </div>
    </div>
  )
}
```

### usePrevious
State の前の状態を保持してくれる Hooks です。
前回の値と比較を行う際など、わざわざ値を記録しておく必要がなくなるので、とても便利です。
```tsx
import { usePrevious } from 'react-use'

export default function App() {
  const [value, setValue] = useState('')
  const prev = usePrevious(value)

  return (
    <div>
      <p>value: {value}</p>
      <p>prev: {prev}</p>
      <input onChange={(e) => setValue(e.target.value)} />
    </div>
  )
}
```

### useCopyToClipboard
テキストをユーザーのクリップボードにコピーします。
`copyToClipboard`にコピーしたいテキストを渡すだけです。`state`に状態を保持しており、コピー出来なかった場合はエラーを入れてくれたり、コピーが出来た場合は value を呼び出せたりします。
```tsx
import { useCopyToClipboard } from 'react-use'

export default function App() {
  const [text, setText] = useState('')
  const [state, copyToClipboard] = useCopyToClipboard()

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button type="button" onClick={() => copyToClipboard(text)}>
        copy text
      </button>
      {state.error ? (
        <p>Unable to copy value: {state.error.message}</p>
      ) : (
        state.value && <p>Copied {state.value}</p>
      )}
    </div>
  )
}
```

### useUpdateEffect
初回レンダー時に実行が無視される useEffect です。
指定した状態だけに依存して実行したい処理を useEffect で記述しても、初回レンダー時は実行されてしまいます。
それを防いでくれるのがこの Hooks です。
```tsx
import { useUpdateEffect } from 'react-use'

export default function App() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const interval = setInterval(() => {
      setCount((c) => c + 1)
    }, 10000)

    return () => {
      clearInterval(interval)
    }
  }, [])

  useUpdateEffect(() => {
    console.log('count', count)
  }, [count])

  return <div>Count: {count}</div>
}
```

## まとめ
react-use で使用できる 便利な Hooks を紹介いたしました。
React Hooks に関しては、react-use だけでなく様々なライブラリが出回っているので、これから実装しようと思っている Hooks が車輪の再発明になっていないか調べてみると良いかもしれません。

また、react-use で使用できる殆どの Hooks が [こちら](https://streamich.github.io/react-use/ "demo") でデモを確認できます。これを触っているだけでも楽しいと思います。
GitHub から実装されている Hooks の中身も確認できるので、こちらも確認してみると多くの学びがあるかと思います。