React Hooks API: useState,useEffect

React Hooks API　を簡単にまとめた。

- useState
- useEffect

他のもおいおいやる。

## useState

functional componentにstateを作る。

`setCount` が呼ばれた時、再レンダリングされる。

```jsx
export default function HookExample() {
  const [count, setCount] = useState(0);

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

## useEffect

レンダー/更新後に実行される。Class Componentのライフサイクルメソッド。

componentDidMount/componentDidUpdate　と異なるのは同期的に行われることなく、イベントとして実行される。
そのため、画面の更新をブロックしない。

Reddit APIと非同期更新を行う例.

```jsx

/**
 * react関連の投稿をredditから取得(非同期処理)
 */
const getReddit = callback => {
  axios
    .get('https://www.reddit.com/r/reactjs.json')
    .then(response => {
      const { data } = response;
      const items = data.data.children.map(item => item.data.title);
      callback(items);
    })
    .catch(err => {
      console.log(err);
    });
};

/**
 * 非同期処理を伴うhookの使用例.
 */
export default function HookExampleWithAsync() {
  const [items, setItems] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  function handeleStatusChange(items) {
    setIsLoading(true);
    setItems(items);
    setIsLoading(false);
  }
  useEffect(() => {
    getReddit(handeleStatusChange); // レンダー後に実行
  }, []);

  return (
    <div>
      <p>reddit Items</p>
      {isLoading ? (
          <p>isLoading</p>
      ) : (
          items.map(item => <div key={item}>{item}</div>)
        )}
    </div>
  );
}

```

## useEffectの第二引数は何入れたらいいの

useEffectは二つの引数をとる。第二引数は副作用が依存している値の配列を渡すことで、値の変更時のみ実行されるようになる。空の配列は依存する値がないことを示す。

例えば、入力値に応じて何かしらデータを取ってくる場合などに有効。

```jsx

/**
 * データソース、ユーザidとユーザ名の配列
 */
const users = [{ id: 1, name: "hoge" }, { id: 2, name: "fuga" }, { id: 3, name: "fuga" }, { id: 4, name: "ppp" }, { id: 1, name: "mu-ga" }]

/**
 * 入力値に対応するユーザ名を返す
 */
const User = (props) => {
  const [user, setUser] = useState({ id: 0, name: 'not Exist' })
  const id = props.id;

  const getUser = (id) => {
    const user = users.filter((user => user.id === id))[0];
    setUser(user ? user : { id: 0, name: 'not Exist' })
  }

  useEffect(() => {
    getUser(id);
  }, [id])
  return (
    <>
      <div>{`username is ${user.name}`}</div>
      <div>{`user id is ${user.id}`}</div>
    </>
  )
}

/**
 *  ユーザidを入力するinputを提供
 */
const App = () => {
  const [id, setId] = useState(0);
  return (
    <>
      <input type="text" onChange={(e) => setId(Number(e.target.value))} />
      <User id={id} />
    </>
  )
}
```

## 副作用のクリーンアップ

購読処理などをuseEffectで登録する場合、購読解除処理関数を戻り値にすることでアンマウント後に実行できる。


以下はReddit APIを10秒ごとにsubscribeする例。

```jsx

/**
 * 
 * 3毎秒ごとにReddit APIを叩く処理
 *
 */
const RedditAPI = function () {
  this.id = null;
  this.subscribe = (callback) => {
    this.id = setInterval((callback) => {
      axios
        .get('https://www.reddit.com/r/reactjs.json')
        .then(response => {
          const { data } = response;
          const items = data.data.children.map(item => item.data.title);
          callback(items);
        })
        .catch(err => {
          console.log(err);
        });
    }, 3000, callback)
  }
  this.unsubscribe = () => {
    console.log('...unscribe')
    clearInterval(this.id)
  }
}

const api = new RedditAPI();

const CleanUpExample = () => {
  const [items, setItems] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const handeleStatusChange = (items) => {
    setIsLoading(true);
    setItems(items);
    setIsLoading(false);
  }
  useEffect(() => {
    api.subscribe(handeleStatusChange); // レンダー後に購読処理開始
    return () => { api.unsubscribe() }; // アンマウント時に購読処理解除
  }, [])

  return (
    <div>
      <p>reddit Items</p>
      {isLoading ? (
        <p>isLoading</p>
      ) : (
          items.map(item => <div key={item}>{item}</div>)
        )}
    </div>
  );
}
```