【React】新機能hooks

# はじめに

会社で色々な事件に巻き込まれしまってなかなか書けなかった不幸な日々でした。
ちょっと乗り遅れた感あるんですが、自分の勉強と理解がてら新しい機能であるhooksを書いていきます。

# hooks
hooksはclassを書くことなくstateとか、色々な機能を使うことができる新しい機能です！React v16.7.0-alphaから使えます。
`create-react-app`でつくると、僕がやってみたときは16.6だったので、ちょっとアップデートしないとできないので注意です。
そして、かつてはSFCと呼ばれる関数定義のコンポーネントたちも、ここではSFCではなくFCになります。
なにせstatelessではないからね・・・！！。


# state
この機能を使うと、functional componentがstateを扱えるようになります。ちゃんとre-renderされようとも値を保持してくれるみたいです。

useStateの書式はこうなります。

```js
const a = useState(b);

a: [currentState, dispatchAction]
b: initialState
```

こんな感じでuseStateは現在値と、setStateに似た関数を返します。

```jsx
import React, { useState } from 'react';

export default () => {
  const [a, b] = useState(0);
  return (
    <div>
      <button onClick={() => { b(a + 1); }}>
        \ovo/ {"<"}{a} times!!
      </button>
    </div>
  )
};
```
ざっとこんな感じになります。

> this.stateと異なり、objectを
使うべきではない
使う必要はない。・・・・しかし、もし望むならそれも可能だ。initialStateは初回だけ使われるであろう・・・・。

的なことが書いてあります。
ということでやってみましょう。

```jsx
import React, { useState } from 'react';

export default () => {
  const [a, b] = useState({
    count: 0,
    str: 'hello',
  });
  const {
    count,
    str,
  } = a;
  console.log(a);
  return (
    <div>
      <button onClick={() => {b({ count: count + 1 })}}>
        \ovo/ {"<"} {count}times!! {str}!!
      </button>
    </div>
  )
};

```

![スクリーンショット 2018-11-02 15.51.34.png](https://qiita-image-store.s3.amazonaws.com/0/121851/c3e60d59-22ea-61aa-7148-4be4f0944557.png)

見事に消滅しましたね。
結果として、第一引数と第一引数を変更する値を保持している関数と言った感じですね。

setStateはマージしてくれるのですが、useStateを使った場合は完全に上書きされるので注意が必要です！
オブジェクトで管理しようとすると自力で保持することが必要になり、自力保持できなくなった瞬間あぼーんするので、複数のstateを使いたい場合は、次の複数宣言をするといいかもしれません。

## Declaring multiple state variables

array destructingを使うことによっていろんな名前を持たせることもできます。
ゆえに、複数のstateを管理したいのであれば、複数回useStateを使えばいいと言うことになります。

```jsx
import React, { useState } from 'react';

export default () => {
  const [a, b] = useState(0);
  const [_a, _b] = useState('!');

  return (
    <div>
      <button onClick={() => { b(a + 1); }}>
        \ovo/ {"<"} {a}times!!
      </button>
      <button onClick={() => { _b(`${_a}!`)}}>
        \omo/ {"<"} hello{_a}
      </button>
    </div>
  )
};
```

と言った感じで従来の書き方だと、こうだったところですね。

```jsx
state = {
  a: 0,
  _a: '!',
};
```



# effect 
データ取得やDOM変更といったside effectsを扱うためにはこのhookを使います。
effect hookは以前からある`componentDidMount`や`componentDidUpdate`、`componentWillUnmount`と同じ目的で使うことができるみたいです。

```jsx
import React, { useState, useEffect } from 'react';

export default () => {
  const [a, b] = useState(0);
  const [_a, _b] = useState('!');

  useEffect(() => {
    alert(`updated! ${a}times!!`);
  });

  return (
    <div>
      <button onClick={() => { b(a + 1); }}>
        \ovo/ {"<"} {a}times!!
      </button>
      <button onClick={() => { _b(`${_a}!`)}}>
        \omo/ {"<"} hello{_a}
      </button>
    </div>
  )
};
```

こうしてみるとわかるんですが、まずcomponentDidmountと同じようなタイミングでuseEffectにある関数が発火しています。
なので、
初回に `updated! 0times!`とアラートが表示されます。

次に、ボタンを押すと、これまたalertが再び発火するといったことができます。

デフォルトだと、renderのたびにeffectが呼ばれることになります。
そして、すでにこのサンプルでお気づきかもしれませんが、現状のままだと、helloの方を押してもuseEffectが発火します・・・。
そこで、第二引数の登場です。

### skipping effect
第二引数に、アレイの形で比較対象を渡すことで、判定をおこないます。

```jsx
  useEffect(() => {
    alert(`updated! ${count}times!!`);
  }, [count]);
```

つまり、こうするんです。
すると、countが変化したかを判定してくれて、`prev === current`であればeffectをスキップするということが可能になっています。

もう少し深掘りしていくと、

```jsx
  useEffect(() => {
    alert(`updated! ${count}times!!`);
  }, [Math.floor(count/3)]);
```

こちらのケースでは3回に1回発火します。

```
count: 1 -> 0
count: 2 -> 0
count: 3 -> 1 (0 -> 1になったので発火)
count: 4 -> 1
count: 5 -> 1
count: 6 -> 2 (1 -> 2になったので発火）
```

小ネタですが、一度だけ動かしたいのであれば`[]`を使うことで一度だけ発火します。

```jsx
  useEffect(() => {
    console.log('------', count);
  }, []);
```
componentDidMountとかはこんな感じですね。

複数の値の検知も行ってくれるので

```jsx
import React, { useState, useEffect } from 'react';

const Sub = (props) => {
  const {
    count,
    hello,
  } = props;

  useEffect(() => {
    console.log('------', count);
  }, [hello, count]);

  return (
    <div>
      {count}
    </div>
  )
};

export default () => {
  const [a, b] = useState(0);
  const [_a, _b] = useState('!');

  return (
    <div>
      <Sub count={a} hello={_a} />
      <button onClick={() => { b(a + 1); }}>
        \ovo/ {"<"} {a}times!!
      </button>
      <button onClick={() => { _b(`${_a}!`)}}>
        \omo/ {"<"} hello{_a}
      </button>
    </div>
  )
};

```

こうすると、countかhelloのどちらかが変更されれば発火するというギミックを組めます。

### cleanup

関数を返すことでクリーンアップの処理を挟むことができます。

```jsx
  useEffect(() => {
    console.log('subscribe', count);
    console.log('---------------');

    return () => {
      console.log('unsubscribe', count)
    };
  });
```

例えばこちらの例。でいくと、ログはこうなります。

```
subscribe 0
---------------  ボタン押した。
unsubscribe 0
subscribe 1
---------------　　ボタン押した。
unsubscribe 1
subscribe 2
---------------　　ボタン押した。
unsubscribe 2
subscribe 3
```

最後に関数を返すと次回、effectが処理を行うときに前もってcleanupを実行してくれるようになるという挙動です。

### Building Your Own Hooks
一応紹介だけしておくと、他のとこでも呼べるよと言う話。

```jsx
import React, { useState, useEffect } from 'react';


const useStatus = () => {
  const [id, setId] = useState(0);
  const newId = id + 1;

  return {
    newId,
    dispatch: setId,
  };
};

export default () => {
  const {
    newId,
    dispatch,
  } = useStatus();

  return (
    <div>
      <button onClick={() => { dispatch(newId)}}>
        \owo/ {"<"} status{newId}
      </button>
    </div>
  )
};

```

`use`という　プレフィックスをつけるとlinterがどうのこうのと書いてありますが基本それだけで動作上はuseとつけて`useStatus`にする必要はないです。
[eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)これ使おうなって話なだけです。
ここら辺の話は、[Rules of Hooks](https://reactjs.org/docs/hooks-rules.html)で書かれてます。


## useContext
contextを使うことができるやつです。
contextに思い入れがないため、感想薄いです。

-- 追記　--
@mrsekut さんからアドバイスいただいてコード更新しました！

```jsx
import React, { useContext, createContext, useState } from 'react';

const context = createContext();
const { Provider } = context;

const Child = () => {
  const count = useContext(context);
  return (
    <div>
      <span>{count.count}</span>
      <button onClick={count.increment}>+</button>
      <button onClick={count.decrement}>-</button>
    </div>
  );
};

export default () => {
  const [count, setCount] = useState(0);
  return (
    <Provider
      value={{
        count,
        increment: () => setCount(count + 1),
        decrement: () => setCount(count - 1)
      }}
    >
      <Child />
    </Provider>
  );
};
```



# Hooks APIs
これが僕がとても楽しみにしていた機能群!

## useReducer

これがとても楽しみでたまらなかった人はたくさんいるのでは・・・？？？？ってぐらい、楽しみだった機能。
仕事が忙しくて触れなくて悲しかった。もっと自由に時間が割ける環境にいこうかなぁ・・・・・。

めっちゃ公式通りで恐縮なんですが、
こんな感じです。

```jsx
import React, { useState, useReducer } from 'react';

const initialState = {
  count: 0,
};

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';

const reducer = (state, action) => {
  const {
    type,
  } = action;
  const {
    count,
  } = state;

  switch (type) {
    case INCREMENT:
      return {
        ...state,
        count: count + 1,
      };

    case DECREMENT:
      return {
        ...state,
        count: count - 1,
      };

    case RESET:
      return initialState;

    default:
      return state;
  }
};

export default () => {
  const [
    state,
    dispatch,
  ] = useReducer(reducer, initialState);
  const {
    count,
  } = state;

  return (
    <div>
      <button onClick={() => { dispatch({ type: INCREMENT }); }}>
        add
      </button>
      <button onClick={() => { dispatch({ type: DECREMENT }); }}>
        del
      </button>
      <button onClick={() => { dispatch({ type: RESET }); }}>
        reset
      </button>
      \owo/ {"<"} {count} happy!!

    </div>
  )
};
```

`useState`にreducerを突っ込んでいる形になりますね。
なので当然こんなこともできちゃいます。

```jsx
  const [
    state,
    dispatch,
  ] = useReducer(reducer, initialState);
  const [
    state2,
    dispatch2,
  ] = useReducer(reducer, initialState);
```

これやる意味があるのかと言われると複雑ですが・・・・。


引数は３つとれて、返り値は２つです。

```
[state, dispatch] = useReducer(reducer, initialState, initialAction);
```

initialActionに書いたものは、初回render後に発火する手筈みたいです。
なので、初期化するときに任意の値を入れ込むと言ったことがここでできますね。

ちょっとreduxと違うところとしては結局useStateに近い概念なところですね。なので、connectみたいな感じでどっからでもstore取ってこれるかというとそうでも無いところがちょっと気になりますね。
これででかい奴を作ったことがないので、ちょっとこれから検討になりますが、ベストプラクティスはなんだろうかと思います。はしご渡しだけは絶対にないとは思ってます。

## useMemo

コールバックをしてくれます。
なんか、こころやさしいおばはんみたいな機能ですかね。

```jsx
import React, { useState, useMemo } from 'react';


export default () => {
  const [count, setCount] = useState(0);
  const newValue = useMemo(() => {
    console.log('Don\'t you forget?', count);

    return `${count}　push`;
  }, [count]);
  
  console.log(newValue);

  return (
    <div>
      <button onClick={() => { setCount(count + 1); }}>
        \owo/ {"<"} {count} times!
      </button>
    </div>
  )
};
```

値が変更されるたびに内部の関数が実行されます。
なので、ボタンを押すとこんな感じで値が出力されます。

```
Don't you forget? 0
0　push
Don't you forget? 1
1　push
Don't you forget? 2
2　push
```

インターフェースは、

```
returnValue = useMemo(function, watchValues);
```

となっています。
これの面白いところとしては値が保持されることです。
watchValuesに何も与えないと、functionが実行されないので試してみます。

```jsx
const newValue = useMemo(() => {
    console.log('Don\'t you forget?', count);

    return `${count}　push`;
  }, []);

  console.log(newValue);
```

```jsx
Don't you forget? 0
0　push
0　push
0　push
```

となります。
初回は実行されて、以後実行されなくなるので、直前に保持された`0 push`がuseMemoの返り値となります。

じゃあ今度は、こうしてみましょう。

```jsx
const newValue = useMemo(() => {
    console.log('Don\'t you forget?', count);

    return `${count}　push`;
  }, [Math.floor(count/3)]);

  console.log(newValue);
```

```
Don't you forget? 0
0　push 
0　push              -> このときcount:1
0　push              -> count:2
Don't you forget? 3 -> count:3　でwatchValuesが更新されるので関数が発火。
3push               -> count: 3を認識して返り値変更
```

となります。

## useCallback

useMemoと基本的には一緒です。
ただし一つだけ違う点があります。

```jsx
import React, { useState, useCallback } from 'react';

export default () => {
  const [count, setCount] = useState(0);
  const newValue = useCallback(() => {
    console.log('Don\'t you forget?', count);

    return `${count}　push`;
  }, [Math.floor(count/3)]);

  console.log(newValue());

  return (
    <div>
      <button onClick={() => { setCount(count + 1); }}>
        \owo/ {"<"} {count} times!
      </button>
    </div>
  )
};

```

返り値が関数になっている点です。
なので

```
console.log(newValue());
```

こうしてます。

## useRef
useRefを使うことでref objectが返ります。それを`ref`属性として渡すことによって、refを扱うことができます。

```jsx
import React, { useState, useRef } from 'react';

export default () => {
  const [count, setCount] = useState(0);
  const buttonElement = useRef(null);

  console.log(buttonElement);

  return (
    <div>
      <button ref={buttonElement} onClick={() => { setCount(count + 1); }}>
        \owo/ {"<"} {count} times!
      </button>
    </div>
  )
};
```

## useImperativeMethods

refに対して処理を紐づけることができます。

```jsx
import React, { useRef, useImperativeMethods } from 'react';

export default () => {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  useImperativeMethods(inputEl, () => ({
    focus: () => {
      console.log('focus');
    },
  }));

  return (
    <>
    <input ref={inputEl} type="text" />
    <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
};

```
このコードでは、inputElにfocusが割り当てられています。
ボタンを押すことによって、focusが実行されるので、useImperativeMethodsのfocusも実行されるという手筈になっています。

いつものように第三引数にwatchValuesを入れることができます。

```jsx
import React, { useRef, useImperativeMethods, useState } from 'react';

export default () => {
  const [count, setCount] = useState(0);
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  useImperativeMethods(inputEl, () => ({
    focus: () => {
      console.log('focus', count);
    },
  }), [Math.floor(count/3)]);

  return (
    <>
    <input ref={inputEl} type="text" />
    <button onClick={onButtonClick}>Focus the input</button>
    <button onClick={() => { setCount(count + 1); }}>
      {count}
    </button>
    </>
  );
};

```
こうすると、countが 0 ~ ２の間は、focusボタンを押したときに

`focus 0`

と出力されますが、countが3のときは　

`focus 3`

が出力されます。


### おまけ
forwardRefがどーたらとドキュメントに書かれたのでforwardRefを使ったサンプルを書きました。
ちょっとややこしいサンプルで申し訳ないです・・・。
こうすると、testボタンを押すと、hiボタンを押したことになります。

```jsx
import React, { useRef, useImperativeMethods, forwardRef } from 'react';

const createButton = (props, ref) => {
  const buttonElement = useRef(null);
  useImperativeMethods(ref, () => ({
    click: () => {
      buttonElement.current.click();
    },
  }));

  return <button ref={buttonElement} onClick={() => { console.log('click child');}}> hi </button>;
};
const CustomButton = forwardRef(createButton);


export default () => {
  const newButtonElement = useRef(null);
  const parentButtonClick = () => {
    newButtonElement.current.click();
  };

  return (
    <div>
      <CustomButton ref={newButtonElement} />
      <button onClick={parentButtonClick}>
        test
      </button>
    </div>
  )
};

```

と言うのも、

```jsx
export default () => {
  const newButtonElement = useRef(null);
  const parentButtonClick = () => {
    newButtonElement.current.click();
  };

  return (
    <div>
      <CustomButton ref={newButtonElement} />
      <button onClick={parentButtonClick}>
        test
      </button>
    </div>
  )
};

```
testボタンは押すと`newButtonElement.current.click()`を実行するように書かれています。

次に、

```jsx
 <CustomButton ref={newButtonElement} />
```

これによって、newButtonElement＝CustomButtonとなります。

```jsx
const createButton = (props, ref) => {
  const buttonElement = useRef(null);
  useImperativeMethods(ref, () => ({
    click: () => {
      buttonElement.current.click();
    },
  }));

  return <button ref={buttonElement} onClick={() => { console.log('click child');}}> hi </button>;
};
c
```

refがわたされたcreateButtonは、渡されたrefをuseImperativeMethosに渡しているため、このような挙動になります。

# おわりに
以上です。
ちょっと感動したものは厚めに、そうで無いものは薄めにといった紹介になってしまいすみません。

このサンプル書くまでは、`useState`の何が嬉しいのかと思ってたんですが、これ書きながら何回も`useState`って書いてたら、こっちの方が楽なきがしてきました。
`useState`, `useEffect`, `useMemo`は使い所がたくさんあると思うのでぜひこれから使っていきたいと思います。
反面`useReducer`すごいけど、これどうやってつかおう・・・。って感じが結構あります。`useContext`と組み合わせて使うのが正かなぁという気がしないでも無いですが自信はないですね・・・。

とりわけrecomposeとかもあまり好きではなかったのでこんな簡単に色々とできるようになってさぞかしSFC、いやFCも幸せだろうと思います！僕もFC多用していきたいと思いますー！