React Redux + React RouterにHooksを導入する

クラスコンポーネントで作成していたReactのプロジェクトを、React Hooksを使用した関数コンポーネントにリファクタリングする際に、React ReduxとReact RouterもHooksに対応させました。
その時に対応の仕方を簡単にですがまとめました。
React Reduxは`v7.1`から、React Routerは`v5.1`からHooksに対応しています。

## 環境

- React : 16.12.0
- React Redux : 7.2.0
- React Router : 5.1.2

## Redux Hooks

React ReduxがHooksに対応したことにより、各コンポーネントにおいて簡単に`dispatch`と`state`を利用できるようになりました。今回は以下の３点について説明します。

- ActionをStoreにDispatchする`mapDispatchToProps`　→　`useDispatch()`
- StoreからStateを取得する`mapStateToProps`　→　`useSelector()`
- ReactコンポーネントとReduxのStoreを紐付ける`connect`　→　`いらない`

`mapDispatchToProps`は`useDispatch()`に、`mapStateToProps`は`useSelector()`を使うことで、Hooksに対応できます。これで各コンポーネントで簡単にdispatchとstateを利用することができます。また`connect()`を使用する必要がなくなります。

### mapDispatchToProps

まずは`mapDispatchToProps`でActionをStoreにDispatchしていたところを、`useDispatch()`で置き換えます。
基本的な使い方は以下のようになります。Actionに登録されている関数を`gateData`としておきます。

- mapDispatchToprops

```jsx
class App extends Component{
    componentDidMount(){
        this.props.gateData();
    }

    render(){
        // JSX
    }
}

const mapDispatchToProps = (dispatch) => {
    return{
        gateData: () => dispatch(gateData())
    }
}
```

- useDispatch()

```jsx
const App = () => {
    // mapDispatchToPropsの部分
    const dispatch = useDispatch();

    useEffect(()=>{
        // this.props.gateData()の部分
        dispatch(gateData());
    })

    return(
        // JSX
    )
}
```

完全に関数コンポーネント内にReduxの実装をまとめることができます。さらに直感的にReduxの機能を使用することができます。

### mapStateToProps

次は`mapStateToProps`でStoreからStateを取得していたところを、`useSelector()`を使用して、Hooksに対応します。
基本的な使い方は以下のようになります。Reducerに登録されているstateを`data`としておきます。

- mapStateToProps

```jsx
class App extends Component{
    render(){
        const { data } = this.props;
        return(
            // JSX
        )
    }
}
const mapStateToProps = (state) => {
    return{
        data: state.data
    }
}
```

- useSelector

```jsx
const App = () => {
    // mapStateToProps & this.propsの部分
    const data = useSelector(state => state.data);

    return(
        // JSX
    )
}
```

一目でわかりますが、useSelectorを使用するだけでStoreからStateを取得することができます。

### 独自Hooksと組み合わせる

Hooks APIとRedux Hooksを組み合わせて、Actionを実行してReducerからStateを取得する独自Hooksを作成します。
例として以下のようなクラスコンポーネントがあったとします。

```jsx
class App extends Component{

    componentDidMount(){
        this.props.getData();
    }

    render(){
        const { data } = this.props;
        return(
            // JSX
        )
    }
}

const mapDispatchToProps = (dispatch) => {
    return{
        getData: () => dispatch(getData())
    }
}

const mapStateToProps = (state) => {
    return{
        data: state.data
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(App);
```

上記のクラスコンポーネントを独自Hooksを利用して、関数コンポーネントで実装します。

```jsx
const useGetData = () => {
    const dispatch = useDispatch();
    const data = useSelector(state => state.data);

    useEffect(() => {
        dispatch(getData());
    })

    return data;
}


const App = () => {

    const data = useGetData();

    return(
        // JSX
    )
}

export default App;
```

Hooks APIとRedux Hooksを利用してActionを実行してStateを返す独自Hooksの`useGetData`と、データを表示するコンポーネント`App`に分離することができます。Reduxを利用してデータを取得するロジック部分をコンポーネントから切り離すことができるので、ロジック部分は他のコンポーネントでも使いまわせます。
コンポーネントごとに、`connect`や`mapDispatchToProps`、`mapStateToProps`を記述する煩わしさからも解放されます。コードもすっきりしますね。

以上がRedux Hooksの使い方でした。

## React Router Hooks

React RouterがHooksに対応したことにより、これまでコンポーネントの`props`から取得していた`history`や`params`を関数を用いて、どこからでも取得することができます。
つまりコンポーネント以外の関数からも使用できるようになります。

以下の3つを説明します。

- useHistory() : ページ遷移で使用する`history`を取得する
- useLocation() : 現在のURLやpathやクエリパラメータを取得する
- useParams() : URLからパスパラメータを取得する

### useHistory

`useHistory()`を使用することで、ページ遷移で使用する`history`を取得することができます。
Hooks未対応の場合、コンポーネントのpropsからhistoryを取得します。そのため、クラス内でしか取得できませんでした。
`useHistroy()`を使うことで、コンポーネント以外でもhistoryを取得することができます。

```jsx
const App = () => {
    const history = useHistory();

    const handleOnclick = (e) => {
        e.preventDefault();
        // 画面遷移
        history.push('/');
        // props.history.push('/');
    }
    return(
        <Link onClick={handleOnclick}>
            Link
        </Link>
    )
}
```

historyを取得できれば、`history.push('/')`や`history.goBack()`を実行することができます。

### useLocation

`useLocation()`を使用することで、現在のページのURLやクエリパラメータを取得することができます。
Hooks未対応の場合、コンポーネントのpropsからlocationを取得し、pathからURLをsearchからクエリパラメータを取得ます。そのため、クラス内でしか取得できませんでした。
`useLocation()`を使うことで、コンポーネント以外でもlocationを取得することができます。

`?id=1&name=sample`というクエリパラメータを設定したとします。

```jsx
const App = () =>{
    // const location = props.location;
    const location = useLocation();
    // URLを取得
    const path = location.path;
    // クエリパスを取得
    const query = location.serach;
    return(
        <>
            <p>{path}</p>
            <p>{query}</p>
        </>
    )
}
```

### useParams

React Routerでは、`<Route exact paht='/url/:id' component={}></Route>`のように、pathに`:~`とすることで、URLからパスパラメータを取得することができます。
クラスコンポーネントでは、コンポーネントのpropsから取得します。
`useParams()`を使うことで、コンポーネント以外でもparamsを取得することができます。

```jsx
const App = () => {
    // URLパラメータを取得
    const {id} = useParams();
    // const id = this.props.match.params.id;
    return(
        <>
            {id}
        </>
    )
}
```

React RouterのRouteの`:~`の~の変数を合わせることで、パスパラメータを取得することができます。

簡単ですが、React Routerで使用した、`useHistory`, `useLocation`, `useParams`の使い方でした。総じてReact RoterがHooksに対応したことで、関数のどこからでも使用することができるようになったことがメリットかなと思います。

## まとめ

簡単にですがReduxとReact RouterのHooks対応をまとめました。
ReduxとReact Router共に、それぞれの機能を関数を呼び出すことで利用できるので、独自Hooksとの親和性が高いのかなと感じました。
全体的にも実装の仕方がシンプルで使いやすく見やすくなってます。
まだまだReact初心者ですが少しづつモダンな実装ができるように勉強していきます。