useEffectでcomponentDidMountを代用する

# componentDidMountをやめる
「hooksで全部書いていきたい！」とはあまり思わないが、
１からFCでコンポーネントを書き始めると、
あとで「componentDidMountいるじゃん」となったときに面倒くさいことが多々あったので、
この際覚えちゃおうというと、いろいろ試してみる。

最終完成品
https://codesandbox.io/embed/dank-shape-mb1mh

# 軽く実装してみた（駄目な例）
codeSandBoxを利用したのをそのまま載せているので、細かいことは大きな心でスルーしてください。

```jsx
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const App = () => {
  return (
    <div className="App">
      <CountButtons initialCount={10} />
    </div>
  );
};

const CountButtons = ({ initialCount }) => {
  const [count, setCount] = useState(initialCount);
  const countUp = () => setCount(count + 1);
  const countDown = () => setCount(count - 1);
  useEffect(() => {
    console.log("useEffect");
  });
  return (
    <>
      <p>{count}</p>
      <button onClick={countUp}>↑↑increment↑↑</button>
      <button onClick={countDown}>↓↓decrement↓↓</button>
    </>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

```

useEffectはこんな感じで書くと、renderするたびに実行されていくようだ。
ボタンをクリックした後の再レンダリングでも発火してしまっているのがコンソールを見るとわかる。
なので、この書き方ではcomponentDidMountの動きは再現できない。

![test2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160041/1ea2d2c4-9a63-c712-6163-4e49709ec701.gif)


# 修正後
では、componentDidMountみたいにするにはどうしたらいいか。
初回レンダリングのときのみ発火させたい。

その時は第２引数に発火させる条件の変数を配列で入れてあげればいいようだ。

```jsx
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const App = () => {
  return (
    <div className="App">
      <CountButtons initialCount={10} />
    </div>
  );
};

const CountButtons = ({ initialCount }) => {
  const [count, setCount] = useState(initialCount);
  const countUp = () => setCount(count + 1);
  const countDown = () => setCount(count - 1);
  useEffect(() => {
    console.log("useEffect");
  }, [initialCount]); // ここにinitialCountを入れる
  return (
    <>
      <p>{count}</p>
      <button onClick={countUp}>↑↑increment↑↑</button>
      <button onClick={countDown}>↓↓decrement↓↓</button>
    </>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
```

こうすると何が起きるのかというと、
初期レンダリング時にpropsで渡されるinitialCountが来て、
その値を見て、useEffectが発火する。

なので２回目以降はinitialCountの値は変わらないので、
ボタンをクリックしてもuseEffectは発火しない
→componentDidMountの出来上がり。

![Jul-08-2019 03-25-24.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160041/a48ddd9d-8780-d4ad-5c86-e448c9f7f97c.gif)

更新ボタンを押したときのみuseEffectがコンソールログに出るので、
ちゃんと初期レンダリングのみ発火するようになっている。

# propsがないとき
propsがあるからcomponentDidMountのように振る舞えているようにも見えるので、
propsがないときのパターンを考えてみる。

useEffectを発火させるのは、初期レンダリングにのみ定義されているものを第２引数に入れればいいので、
setCountをいれて試してみる。

```jsx
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const App = () => {
  return (
    <div className="App">
      <CountButtons />
    </div>
  );
};

const CountButtons = () => { // propsを消す
  const [count, setCount] = useState(10);
  const countUp = () => setCount(count + 1);
  const countDown = () => setCount(count - 1);
  useEffect(() => {
    console.log("useEffect");
  }, [setCount]); // useStateで定義した関数を入れてみる
  return (
    <>
      <p>{count}</p>
      <button onClick={countUp}>↑↑increment↑↑</button>
      <button onClick={countDown}>↓↓decrement↓↓</button>
    </>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
```

こんな感じにしておけばいいのではと思い実行。

![test1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160041/009851fa-b516-9e60-81b8-c41eeec08328.gif)

できた。
なるほどね。

# 注意点
いろいろなサイトでこのuseEffectについて調べていたが、
第２引数に空配列```[]```を入れても動くみたいだけど、アンチパターンらしい。
よくわからないが、人的ミスを起こしやすくなるとか。

# componentDidMountでよくね？
useEffectでcomponentDidMountの振る舞いをできるように実装してみたが、
useEffect自体がたくさんの処理を行えるので、
どんな処理がなされているのかを細かく見ないと、修正するのが大変になりそう。

それこそバグの原因にないかねない気がする。

実際につかっていって、これは使わないほうがいいわってなったときは
また記事にでもしましょうか。。。

# 追記
ラップすればcomponentDidMountとかは簡単に判別できるようになることに気がついた。。。

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

export const useDidMount = (func: Function) => useEffect(() => { func() }, []);
export const useWillUnMount = (func: Function) => useEffect(() => func, []);
```