---
# System prepended metadata

title: ReactHooks

---

# ReactHooks

```typescript=
import { useState, useEffect, useCallback } from 'react';
import fetch from 'isomorphic-unfetch';
import ck from 'camelcase-keys';
import qs from 'qs';

import { T as Status, Initial, Loaded, Failed, ErrorMessage } from '../utils/Status';
// resourceはサーバーサイドの持ってるデータ
// T = Resurce
type Return<T> = [Status<T>, () => void];

// Resource in <Resource extends object>(endpoint: string, params?: object):
// 以下大きなカスタムフック
// カスタムフックの引数にオブジェクトのデフォルト引数は与えてはいけない
export default <Resource extends object>(
  endpoint: string,
  // 使う側では第一引数にいれる
  params?: object
  // 使う側では第二引数にオブジェクトのままいれる
): Return<Resource> => {
  const [resource, setResource] = useState<Status<Resource>>(new Initial);
  // useState<Status<Resource>>は初期値に(new Initial)を持ったresourceという状態
  // その変更にはsetResourceを使う
  const [refreshCount, setCount] = useState<number>(0);
  // useState<number>は初期値に(0)を持ったrefreshCountという状態
  // その変更にはsetCountを使う
  const url = params ? `${endpoint}?${qs.stringify(params)}` : endpoint;
  // urlはparamsが渡されたらエンドポイント＋クエリーパラメーター
  // 渡されなかったらエンドポイントがそのままurl
  useEffect((): void => {
    const fetchResource = async (): Promise<void> => {
      const res = await fetch(url);
      if (res.status !== 200) {
        return setResource(new Failed(ErrorMessage.LoadingError));
      }
      // statusが200でないならエラーを返す
      const json = await res.json()
        .then(json => ck(json, { deep: true }) as Resource);
      setResource(new Loaded(json));
    };
     // レスポンスのjsonを解析
    fetchResource().catch(() => {
      setResource(new Failed(ErrorMessage.LoadingError));
    });
    // fetchResourceをcatchしたらエラーメッセージを返す
  }, [url, refreshCount]);
  // urlが変更された時、refreshCountが有効になった時にuseEffect
  const refresh = useCallback(() => {
    setCount(prev => prev + 1);
    // 前の状態よりも+ 1の場合
  }, [setCount]);
  // 前の状態よりも+ 1の場合setCountに保存される(更新される)
  return [resource, refresh];
  // その時に[resource, refresh]を返す
};
```