1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

react-hooks で redux を書いてみた話

Last updated at Posted at 2020-01-25

react-hooksでreduxを真剣にいじる機会に恵まれないので、練習がてらボタンをおしたら数字が増えるカウンターを作ってみました

環境構築

まずは環境構築。絶対に必要なredux関連と型情報をワンセットでインストールしましょう。

npx create-react-app hooks --template typescript
npm install react-redux 
npm install @types/react-redux
npm install redux
npm install @types/redux

生成されたテンプレートのプロジェクトをいじっていきます。

action

まずはactionから定義をします。
今回実装するアクションは以下の通りです。

  • カウントが増える INCREMENT
  • カウントが減る DECREMENT
export  const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export function increment() {
    return {
        type: INCREMENT
    }
}

export function decrement() {
    return {
        type: DECREMENT
    }
}

reducer

次に実際に値を更新したり、編集したりするreducerを実装します。

import {INCREMENT, DECREMENT} from "./action";

const initialState = {
    count: 0
}

function reducer(state = initialState, action: {type: string}) {
    switch(action.type) {
        case INCREMENT: 
            return {
                count: state.count + 1
            };
        case DECREMENT: 
            return {
                count: state.count - 1
            };
        default: {
            return {
                count: 0
            }
        }
    }
}

export default reducer;

普通であれば、 combinereducer で結合しますが、今回はreducerを一つしか作らないので、生のreducerをそのままexportしています。

store

値を格納するstoreです。
reducerを受け取ってstoreを生成していますが、このままだとstateの型が不定で後々のつなぎこみで不都合が生じるので、RootStateを定義しています。


import {createStore} from "redux";
import reducer from "./reducer";

export type RootState = ReturnType<typeof reducer>
export const store = createStore(reducer);

ReturnType は関数の戻り値の型として生成して返します。今回であれば、reducerの戻り値(stateのproperty構造)を型として返してくれます。

つまり、今回のRootStateの中身はこのような感じ


type RootState = {
    count:number
}

connect(component)

hooks を使ったものはこの部分が大きく違っています。いままでは、
mpaStateToProps
mapDispatchToProps
connect

を使ってましたが、

useSelector : storeからstateを取得
useDispatch : storeへ変更を伝えるためのdispatch関数を取得

↑の二つを使うだけでよくなります。

先程、作った RootStateuseSelectorに使います。 stateから情報を受け取るのですが、この時stateの情報がないと、型が解決できなくなるので、 RootState を作成して型を確定させるのです。

import React from "react";
import { useSelector, useDispatch} from "react-redux";
import {RootState} from "./store";
import {increment, decrement} from "./action";

export const Counter: React.FC = () => {
    const dispatcher = useDispatch();
    const state = useSelector((state:RootState) => state);

    return (
        <div className="counter">
            <h1> counter </h1>
            <p>{state.count}</p>
            <button onClick={() => dispatcher(increment())} >count up</button>
            <button onClick={() => dispatcher(decrement())} >count down</button>
        </div>        
    )
}

つなぎ込み

あとは、 Providerにstoreを渡して終了です。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {store} from "./store";
import {Provider} from "react-redux"

ReactDOM.render(<Provider store={store}> <App /> </Provider>, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

最後に

mpaStateToProps
mapDispatchToProps
connect

という黒魔術を使わなくてよくなり、且つhooksの文法をしっていれば処理の流れを追いやすくなったと思います。
というわけで、時間があれば昔のreactコードのリファクタをしたいと思います。

それでは、よいreactライフを

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?