7
6

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 5 years have passed since last update.

Reduxの簡単なコードを書きながら、処理の流れを整理してみた

7
Last updated at Posted at 2020-10-06

はじめに

Reduxを最初に勉強する際に、まず登場する用語が、多くて、良く分からんとなりがちだと思います。少なくとも自分はそうです(泣)

まだまだ勉強中ではあるものの、一旦、整理する意味で、Reduxの処理の流れを言語化してみたいと思い、記事にまとめました。

※今は、Redux Toolkitを使うのが普通なのかもしれませんが、そこには触れていません。Redux Toolkitに関しては、追ってアウトプットしようと思っています。

Reduxを使用する中で登場する用語の整理

まず登場する用語の整理から...
State
アプリケーションの状態。

Store
Stateの状態を保持している場所。

Action
ユーザーがアプリケーションで何をしたいかという情報を持つプレーンなオブジェクト。
コンポーネントから発行されたActionは、Dispatcherによって、Reducerに渡す。

Reducer
Actionを元にStateを更新する副作用の無いメソッド。

Reduxの処理の流れ

整理する意味でも図化してみました。
① コンポーネントが、Action Creatorを呼び出し、Actionを取得します。
② 取得したActionをReducerに渡します。これをdispatchといいます。
③ Reducerは渡されたActionに応じてStateを更新します。
④ コンポーネントは、Stateに変更があった場合は、該当のUIを描画し直します。

何だか、手続きが、面倒くさいように感じますが、そこがポイントです。
決まった手続きでしか変更を加えられない、状態に対して定義された変更しか加えられないルールは、予期せぬ更新処理を防ぎ、コードを予測しやすくしてくれます。

Action Creatorも重要な役割を果たしています。Reducerに対しては決められた属性、値を持つActionを渡さなければいけないのですが、間違ったActionを渡さないよう、Action Creatorを介して、Actionを取得するようにします。

reduxFlow

では、実際にそれぞれの役割で書かれる処理を書いてみましょう。
よくあるカウンターですが、何となくイメージの手助けになれば、と思います。

ユーザーがアプリケーション上で何らかの操作をし、Actionが発行される

ユーザーの操作によって、コンポーネントから、Actionが生成されます。
後述しますが、コンポーネント上で、この定義ファイルをimportして利用することで、コンポーネントからActionの作成依頼をかけられます。

Actionというディレクトリを切って、そこにActionを発行するメソッドを定義しておきます。
以下は Action Creator と呼ばれるものです。dispatchする時に生の値を渡すのでなく、Action Createrの関数の戻り値を使います。これは、バグを防ぐために有効です。

Redux StyleGuide

//Actionはどんなイベントが起こったかを表現するプレーンなオブジェクトです

export const increment = () => {
  return {
    type: "INCREMENT"
  }
}

export const decrement = () => {
  return {
    type: "DECREMENT"
  }
}

actionをdispatchする際は、生の値を渡すのでなく、Action Creatorの戻り値を使う方が安全です。
※以下のように、生の値を渡さないこと。

<button onClick={()=> dispatch({type: "INCREMENT"})}>click</button>
<button onClick={()=> dispatch({type: "DECREMENT"})}>click</button>

以下のように、Action Creator関数の戻り値を使いましょう。

<button onClick={()=> dispatch(increment())}>click</button>
<button onClick={()=> dispatch(decrement())}>click</button>

発行されたactionをdispatchする

Actionは生成しただけだと意味がなくて、Dispatchしないと、Store内の値を変更することはできません。
コンポーネント内で、Actionをimportして、ActionをDispatchしましょう。

※Classコンポーネントが主流だった時代は、connect関数が利用されていたようですが、現在は、useSelectoruseDispatchを使って、ReactとReduxの接続が可能なので、こちらを積極的に使っていった方が良さそう。

↓useSelectorを使って、Storeからstate(値)を取得し、useDispatchにAction Creatorを渡して、ActionをDispatchします。


import React from 'react';
import { increment, decrement } from 'Action/actionCreator';
import { useSelector, useDispatch } from 'react-redux';

function App() {

  const count = useSelector(state => state.count);
  const dispatch = useDispatch()

  return(
    <>
    <div>{count}</div>
    <button onClick={()=> dispatch(increment())}>click</button>
    <button onClick={()=> dispatch(decrement())}>click</button>
    </>
  )
}

export default App;

DispatchされたActionによってReducerでStateを更新する

ここでは、Reducerディレクトリを切って、reducer.jsに以下の記述を書いています。
Reducerの関数は二つ引数をとります。第一引数にstate、第二引数にはactionがreturnした値をとります。
(ここでは、単純な処理しか書いてませんが、typeとpayloadを書けます)

第一引数のstateですが、基本的には現在の引数の状態を受け取るようになっていますが、もし現在のstateの状態が指定されていない場合は、デフォルトの値をstateの引数に持たせるようにします。

export const initialState = { count: 0 };

export const countReducer = (state = initialState.count, action) => {
  switch(action.type) {
    case 'INCREMENT':
      return {
        ...state, count: state.count + 1
      };
    case 'DECREMENT':
      return {
        ...state, count: state.count - 1
      }
    default: {
      return state;
    } 
  }
}

Storeを作成してpropsとして渡す

Reducerが作成できたら、Reduxの核となるStoreを初期化して、Propsとして渡しましょう。
Storeは1アプリケーションにつき、一つです。引数には、reducerと初期値を渡します。

const store = createStore(reducer, initialState);

最後にStoreを最上位のコンポーネントでProviderを使って、Storeをpropsとして渡します。これで初めて、子孫コンポーネントの中でReduxの機能が使えるようになります。

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { initialState, reducer } from './Reducer/reducer';

const store = createStore(reducer, initialState);

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

まとめ

以上で一通りのReduxに必要な処理が揃ったことになります。上述のuseSelectoruseDispatchによって、ReactとReduxの接続は実現できています。ユーザーのアクション(ボタンのクリックなど)の応答として、更新された値が表示されます。

まだまだ理解の浅い部分が沢山ありますが、Reduxのディレクトリ構成(Ducks,re-ducks等)や、もっとすっきり書ける方法(redux toolkit)もキャッチアップして理解を深めていきたいな思います(楽して書きたい)

7
6
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
7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?