LoginSignup
14
16

More than 3 years have passed since last update.

ReduxとReact-Reduxの超入門チュートリアル

Last updated at Posted at 2020-03-12

やっとReduxが理解できたので、忘れないうちにチュートリアル形式でまとめておきます。

学習の前に

まず理解すること

  • ReduxReact-Reduxは違うもの
    • Reduxは、設計方法
    • React-Reduxは、ReduxとReactをつなぐもの

これを頭に入れておくと、今後の理解がスムーズになるかと思います。

では早速、Reduxを使ってみましょう。

Redux

いったん、Reactのことは忘れてReduxのみに触れていきましょう。

まずはシンプルに使ってみる

適当なディレクトリを作成し、以下を実行してください。

$ npm init -y
$ npm i redux

これで、Reduxが使えるようになりました。
続いて、index.jsを作成し、以下を記述します。

// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch({ type: 'INCREMENT' });

Node.jsindex.jsを実行します。

$ node index.js
// -> 1

ターミナルに1が表示されれば、成功です。

これで、あなたもReduxが使えるようになりました!
おめでとうございます (^^)

では、ReducerStoreSubscribeDispatch部分を、それぞれ解説します。

Reducer

Reducerは、状態管理したい値(state)と、その値を変化させるための処理が書かれている関数です。
この時点では、まだReduxとはなんの関係もありません。ただの関数です。

function reducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

第1引数にstate、第2引数にactionを入れ、switch 文を使い、actionの値によって、stateの値を変えてreturnします。

stateには、状態管理したい値の初期値を代入しておきます。今回の例では、数値の0を代入しています。

actionは、後に出てくるstore.dispatch({ type: 'INCREMENT'})メソッドの引数{ type: 'INCREMENT' }が渡されてきます。

Store

Storeは、stateを管理するためのオブジェクトです。
createStore()の引数にReducerを入れる事によって作成できます。

詳しくは後述しますが、store.getState()stateの現在の状態を取得したり、store.dispatch()で状態を変化させたりすることができます。

const store = require('redux').createStore(reducer);

npm i reduxでインストールしたreduxrequireし、createStore()メソッドを呼び出しています。
そして、createStore()の引数に、上で解説したReducerの関数を入れ、store定数に代入しています。

storeはオブジェクトになっていて、stateの取得や、その値を変化させることができるstore.dispatch()を使用することができます。

Subscribe

store.subscribe(() => console.log(store.getState()));

store.subscribe()メソッドは、store.dispatch()が呼び出されたら、引数のコールバック関数を実行します。

そして、store.getState()メソッドは、現在のstateを取得します。

store.subscribe()の引数のコールバック関数で、console.log(store.getState())を実行しているため、store.dispatch()するたびに、現在のstateをコンソールに表示します。

Dispatch

store.dispatch({ type: 'INCREMENT' });

store.dispatch()の引数に入れた値が、Reducerのactionに渡されます。

よって、上の例では、{ type: 'INCREMENT' }reducer関数のactionに渡されるので、switchaction.type'INCREMENT'の文字列になり、case 'INCREMENT'が実行され、state + 1returnされて、stateの値が0から1に変化します。

以下のコメントアウト付きReducerを見ながら、動きを確認してみましょう。

/**
 * state に状態管理したい値を代入する。
 * store.dispatch({ type: 'INCREMENT' })が実行された場合、
 * action には { type: 'INCREMENT' } が入っている。
*/
function reducer(state = 0, action) {
  /** 
   * store.dispatch({ type: 'INCREMENT' })が実行された場合、
   * action.type は 'INCREMENT'
  */
  switch (action.type) {
    case 'INCREMENT':
      return state + 1; // こちらが実行され、もともとの state = 0 に +1 されて 1 が return される
    default:
      return state;
  }
}

returnされたstateは、storeで保持され、store.getState()で取得することができます。

Actions と Action Creators

Rreducercaseで指定した文字列は、通常Actionとして切り分けます。
また、store.dispatch()の引数の{ type: 'INCREMENT' }も、Action Creatorsとして、関数にします。

// Actions
const INCREMENT = 'INCREMENT';

// Action Creators
function increment() {
  return {
    type: INCREMENT
  }
}

// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT: // Action の INCREMENT に置き換え
      return state + 1;
    default:
      return state;
  }
}

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch(increment()); // Action の INCREMENT に置き換え

Actionsとして切り分けたことにより、記述量は増えてしまいますが、ファイルを分けてimportexportするときに、使い勝手が良くなります。

Action Creatorsは、関数の引数のオブジェクトにtype以外を追加することができるので、Reducerでの処理をコントロールしやすくなります。

たとえば、以下のようにincrement()関数の引数の数値によって、カウントアップする数値を柔軟に変更させることができます。

// Actions
const INCREMENT = 'INCREMENT';

// Action Creators
function increment(count) { // 引数を追加
  return {
    type: INCREMENT,
    count // 引数を return するオブジェクトに追加する
  }
};

// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT:
      return state + action.count; // action に count が追加される
    default:
      return state;
  }
};

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch(increment(2)); // ここで state は 2 になる
store.dispatch(increment(3)); // さらにここで state は 5 になる

ここまで理解できれば、ReduxベーシックチュートリアルData Flowまでは理解できるかと思います。
(英語はがんばってくださいw)

React-Redux

さて、ここまではReduxについて学んできました。
ここからは、いよいよReduxReactをつなげるReact-Reduxの出番です!

とりあえず、create-react-appでReactのプロジェクトを作りましょう。

適当なディレクトリで、以下を実行してください。

$ npx create-react-app redux-example
$ cd redux-example && npm start

localhost:3000でReactのプロジェクトが立ち上がればOKです。

Redux と React-Redux のインストール

ReduxとReact-Reduxをインストールします。

$ npm i redux react-redux

src/reducers.jssrc/actions.jsを作成し、Reduxを学んだときに作成した、index.jsActionsAction CreatorReducer部分をそれぞれコピペして、少し修正します。

src/actions.js

// Actions
export const INCREMENT = 'INCREMENT'; // exportして外部で使用できるように修正

// Action Creators
export function increment(count) { // exportして外部で使用できるように修正
  return {
    type: INCREMENT,
    count
  }
};

src/reducers.js

import { INCREMENT } from './actions'; // Actionsをimport

// Reducer
export default function reducer(state = 0, action) { // export defaultして外部で使用できるように修正
  switch (action.type) {
    case INCREMENT:
      return state + action.count;
    default:
      return state;
  }
};

Storesrc/index.jsに書いてみましょう。ここでReact-Reduxを使い、ReactとReduxをつなぎます。

以下のように、src/index.jsを書き換えてみてください。

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'; // createStoreをimport
import { Provider } from 'react-redux'; // Providerコンポーネントをimport
import reducer from './reducers'; // reducer関数をimport
import App from './App';

const store = createStore(reducer); // storeオブジェクトを作成

ReactDOM.render(
  // <App />コンポーネントを<Provider store={store}></Provider>でラップする
  <Provider store={store}>
    <App />
  </Provider>
, document.getElementById('root'));

<Provider store={store}></Provider>でラップされたコンポーネント内であれば、Storeにアクセスすることができます。

これで、React上でReduxを使う準備が整いました。

続いて、Redux Hooksを使い、stateの取得と変更をしてみましょう!

Redux Hooks

Redux Hooksの登場により、簡単にReduxStoreにアクセスできるようになりました。src/App.jsuseSelectoruseDispatchを使用し、stateの取得と変更をしてみましょう。

src/App.jsを以下のように書き換えてみてください。

src/App.js

import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; // これがRedux Hooks
import { increment } from './actions'; // Action Creatorsのincrement関数をimport

function App() {
  const count = useSelector(state => state); // useSelector で state を取得
  const dispatch = useDispatch(); // useDispatch で state を更新

  return (
    <div className="App">
      {/* 現在のstateを表示 */}
      <div>{count}</div>
      {/* クリックするたびに、stateを更新 */}
      <button onClick={() => dispatch(increment(1))}>INCREMENT</button>
    </div>
  );
};

export default App;

localhost:3000stateの値が表示されているかとい思います。また、その下の「INCREMENT」ボタンをクリックすると、数値が書き換わるはずです。

useSelectorを使うと、storeからstateを取得することができます。後述する、useDispatchによるdispatch()が実行されると、stateの比較が行われ、変更があれば再レンダリングされます。

Reduxのときに出てきた、store.subscribe(() => console.log(store.getState()));をやっている感じですね。

useDispatchを使うと、store.dispatch()にアクセスすることができます。

こちらも、Reduxのときに出てきた、store.dispatch(increment());ですね。

おわりに

かなりシンプルでしたが、ReduxRedux-Reactのチュートリアルでした。

ここまで理解できれば、ReduxReact-Reduxのドキュメントはだいぶ理解できるようになるかと思います。まだまだ解説していない部分もありますので、そこは一緒に学習していきましょう。

少しでも、皆様の学習の手助けになれば、これ幸いです。

それでは、また。

14
16
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
14
16