React Hooksの主な機能

Udemyのこちらのコースを参考にしています。「[React Hooks 入門 - Hooksと Redux を組み合わせて最新のフロントエンド状態管理手法を習得しよう！](https://www.udemy.com/course/react-hooks-101/)」

## useState

classコンポーネントの`this.state`、`setState`と同じ機能

``` javascript
const [{state名}, {stateを変更するときの関数名} = useState({初期値})]

// 基本的な書き方
const [count, setCount] = useState(0);

// 初期値はobjectでも書ける
const [statem, setState] = useState({
  name: 'ichiro',
  age: 51,
});
const { name, age } = state;
```

``` javascript:Sample.js
import React, { useState } from 'react';

const Sample = () => {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>count: {count}</p>
      <button type="button" onClick={() => setCount(count + 1)}>Click</button>
    </>
  );
};

export default Sample;
```

## useEffect

classコンポーネントのライフサイクルメソッドと同じ機能

``` javascript
// componentDidMountとcomponentDidUpdateと同じ挙動
useEffect(() => {
  // 処理を書く
});

// componentDidMountと同じ挙動
useEffect(() => {
  // 処理を書く
}, []);

// hogeの値のみcomponentDidMountとcomponentDidUpdateと同じ挙動
useEffect(() => {
  // 処理を書く
}, [hoge]);
```

``` javascript:Sample.js
import React, { useState, useEffect } from 'react';

const Sample = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    alert('stateのcountが更新されたよ');
  }, [count]);

  return (
    <>
      <p>count: {count}</p>
      <button type="button" onClick={() => setCount(count + 1)}>Click</button>
    </>
  );
};

export default Sample;
```

## useReducer

reducerにdispatchして更新されたstateを取得する

``` javascript
const [{現在のstate}, {dispatch関数}] = useReducer[{reduce関数} , {初期値}]
const [state, dispatch] = useReducer(reducer, []);
```

``` javascript:Sample.js
import React, { useReducer } from 'react';

const Sample = () => {
  const reducer = (state = [], action) => {
    switch(action.type) {
      case 'CREATE':
        const personal = { name: action.name, age: action.age }
        return [...state, {...personal}];
      default:
        return state;
    };
  };

  const create = () => {
    dispatch({
      type: 'CREATE',
      name: 'ichiro',
      age: 51,
    });
  };

  const [state, dispatch] = useReducer(reducer, []);

  return (
    <>
      <button type="button" onClick={create}>Click</button>
      <ul>
        {
          state.map((s, index) => (
            <li key={index}>
              <p>名前は{s.name}</p>
              <p>年齢は{s.age}</p>
            </li>
          ))
        }
      </ul>
    </>
  );
};

export default Sample;
```

## useContext

- reduxのconnect関数に変わるもの
- Providerでwrapされたcomponentは、props valueのデータをバケツリレーなしで利用できる

``` javascript:AppContext.js
// ProviderとConsumer(useContext)を利用できるようにcreateContextを読み込む
import { createContext } from 'react';

const AppContext = createContext(); 

export default AppContext;
```

```javascript:App.js
import React, { useReducer } from 'react';
import AppContext from './AppContext';
import Sample from './Sample'

const App = () => {
  const reducer = (state = [], action) => {
    switch(action.type) {
      case 'CREATE':
        const personal = { name: action.name, age: action.age }
        return [...state, {...personal}];
      default:
        return state;
    };
  };

  const [state, dispatch] = useReducer(reducer, []);

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      // Sample comonentにstateとdispatchがバケツリレーなしで利用可能に
      <Sample />
　　</AppContext.Provider>
  )
}

export default App;
```

``` javascript:Sample.js
// Providerのvalueのデータを受け取るために`useContext'を読み込む
import React, { useContext } from 'react';
import AppContext from './AppContext';

const Sample = () => {
  const { state, dispatch } = useContext(AppContext);

  const create = () => {
    dispatch({
      type: 'CREATE',
      name: 'ichiro',
      age: 51,
    });
  };

  return (
    <>
      <button type="button" onClick={create}>Click</button>
      <ul>
        {
          state.map((s, index) => (
            <li key={index}>
              <p>名前は{s.name}</p>
              <p>年齢は{s.age}</p>
            </li>
          ))
        }
      </ul>
    </>
  );
};

export default Sample;
```

## useMmemo

以下、公式サイトからの引用です。

> “作成用” 関数とそれが依存する値の配列を渡してください。useMemoは依存配列の要素 のひとつが変化した場合にのみメモ化された値を再計算します。この最適化によりレンダー毎に高価な計算が実行されるのを避けることができます。

> useMemoに渡した関数はレンダー中に実行されるということを覚えておいてください。レンダー中に通常やらないようなことをこの関数内でやらないようにしましょう。例えば副作用は useMemo ではなく useEffectの仕事です。

以下「[雰囲気で使わないReact hooksのuseCallback/useMemo](https://qiita.com/seya/items/8291f53576097fc1c52a)」からの引用になります。

> useMemoを使うとパフォーマンスが上がる、値を保持する処理は全てuseMemoを使うのかと思ったんですが、何かしらの複雑な計算のみに使用するようです。

```javascript
const memoedValue = React.useMemo(() => /* 何かしらの複雑な計算 */, [])
```

複雑な計算ってどれくらい？
と思いますよね？

この処理を超えるときはuseMemoを使う

```javascript
function is(x: any, y: any) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y)
  );
}
```

## useCallback

- 子コンポーネントのpropsに処理を渡すときに使用
- 親コンポーネントのrenderの度に処理が発生するのを回避するために「deps」で指定した値に変化があったときだけ処理を行なう
- onClick属性で関数を書くとき、event時のみに処理を行わせる`.bind`と同じという認識

### depsに指定する要素
どの要素を指定すればいいのか分からなくなるときがあるのでESLintを使う方法あり

以下「[雰囲気で使わないReact hooksのuseCallback/useMemo](https://qiita.com/seya/items/8291f53576097fc1c52a)」からの引用になります。

> 基本的なプラクティスとしては「useCallback/useMemoの中で参照する要素は全て depsに書く」です。とりあえずそうしておけば必ず最新の値には保たれます。
また、このリスクを軽減する方法としてeslintのexhaustive-depsがあります。


## lazy/Suspense

- lazyは動的componentに適用
- Suspense(動的componentの待機中の処理)と併用

lazyについて公式サイトから引用

> React.lazy 関数を使用すると、動的インポートを通常のコンポーネントとしてレンダリングすることができます。
※React.lazyとSuspenseはまだサーバーサイドレンダリングには使用できません。

Suspenseについて公式サイトから引用

> 遅延コンポーネントは、Suspense コンポーネント内でレンダーされる必要があります。これによって、遅延コンポーネントのローディングの待機中にフォールバック用のコンテンツ（ローディングインジケータなど）を表示できます。

``` javascript:Sample.js
import React, { lazy, Suspense } from 'react';

const Hoge = lazy(() => import('./Hoge'));

const Sample = () => (
  <>
    <Suspense fallback={<div>loading...</div>}>
      <Hoge />
    </Suspense>
  </>
);
```