LoginSignup
5

More than 3 years have passed since last update.

ReduxとReduxToolkitを使用してReact内でデータを管理する

Last updated at Posted at 2020-08-10

はじめに

前回のReactを使用してWeb画面を作成するでは、Reactの簡単なサンプルと共に説明をまとめました。次はデータを持ちまわすためにReduxとReduxToolkitの簡単なサンプルと説明をまとめようと思います。
最低限の簡単なサンプルなのでアクションは別ファイルにするべきであったり、といったベストプラクティス的なものは他のサイトで調べてください。

環境

  • node.js: v12.18.2
  • webpack: 4.44.1
  • React: 16.13.1
  • Redux: 7.2.1

環境作成

前回のReactを使用してWeb画面を作成するの続きとなります。
環境構築などはそちらを見てください。

Redux

Reactのコンポーネント間でデータを共有するためにReduxというライブラリを使用します。

Reduxのインストール

ReduxのライブラリとReduxをシンプルに記載するためのtoolkitをインストールします。


npm install react-redux
npm install @reduxjs/toolkit

シンプルなRedux

Reduxのソースの作成

フォルダの作成

Reduxのソースをstore、slice、コンポーネントに分けて作成するので、コンポーネント以外のフォルダを作成してください。コンポーネントは前回作成したsrcの下に入れます。


project_root
├─src    // reactのJavaScriptファイルやCSSファイルを格納
├─store  // redux toolkitのstore(reduxのstoreをまとめたもの)ファイルを格納
  ├─slice    // redux toolkitのslice(reduxのactionとreducerをまとめたもの)

sliceファイルの作成

内部的に保持する情報と処理をまとめたものをsliceファイルとして作成しています。
今回は単純にWeb画面と文字のやり取りをするため保持するデータは"mess"、処理は"hello"をデータに置き換える処理としています。

messageSlice.js

import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

export const messageSlice = createSlice({
  // slice名
  name: 'message',
  // 内部で保持するデータ(キー:mess, 初期値:メッセージ)
  initialState: {
      "mess": "メッセージ"
  },
  // 内部のデータにアクセスするための処理(処理名:sayhello)
  reducers: {
    sayhello: state => {
      state.mess = "hello";
    }
  },
});

// 外からインポートするためにactionとreducerをエクスポートする
export const { sayhello } = messageSlice.actions;
export default messageSlice.reducer;

storeファイルの作成

先ほど作成したsliceのreducerをstoreに登録することで各コンポーネントで情報を共有できるようにします。

store.js

import { configureStore } from '@reduxjs/toolkit';
import messageReducer from './slice/messageSlice';

export default configureStore({
  reducer: {
    message: messageReducer,
  },
});

storeをコンポーネントに適用させる

コンポーネント間で情報をやり取りするために先ほど作成したstoreをreactのrenderに登録します。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// redux用のインポート
import { Provider } from 'react-redux'
import store from '../store/store'

ReactDOM.render(
  // インポートしたstoreを登録する
  <Provider store={store}>
    <App />,
  </Provider>,
  document.getElementById('app')
);

slice経由でstoreを使用する

コンポーネントで情報を処理するためにsliceのaction経由でstoreを操作します。

App.js

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { sayhello } from '../store/slice/messageSlice';

export function Message() {
  // store内の値を取得
  const message = useSelector(state => state.message.mess);
  // actionを操作するための関数取得
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        {/* Sliceで定義したactionをdispatch経由で呼び出す */}
        <button aria-label="hello" onClick={() => dispatch(sayhello())}>
          こんにちは
        </button>
        {/* 上で呼び出したmessageを表示する */}
        <span>{message}</span>
      </div>
    </div>
  );
}

最終的なフォルダ構成


project_root
├─dict
├─public
|  Ⅼ-index.html
├─src    
|  ├─App.js
|  Ⅼ─index.js
├─store  
|  ├─slice  
|  | Ⅼ─messageSlice.js
|  Ⅼ-store.js
├─.babelrc
├─package.json
Ⅼ─webpack.config.js

Reactの実行

前回同様にReactを開発用のサーバで起動してブラウザからアクセスしてください。


"./node_modules/.bin/webpack-dev-server"

表示されたWeb画面にこんにちはというボタンがあると思うため、それをクリックすると隣の文字がhelloに変わります。
内部の処理としては、ザックリ言うと以下のようなイメージになります。
画面描画時にApp.js内でuseSelectorを使用することにより、messageSlice.jsで定義したmess変数を呼び出しています。
その呼び出した変数を<span>{message}</span>と紐づけて変数が変わったら自動的に変わるように使用しています。
ボタンを押したらmessageSlice.jsで定義したsayhelloを呼び出してmess変数を更新して、再描画しています。

画面から受け取るRedux

先ほどはactionの中で定義した値に更新していましたが、今度はWeb画面に入力した内容を使用してstoreを更新します。

Reduxのソースの作成

先ほどのファイルに処理を追加して機能を実装します。

sliceファイルの作成

messageSlice.js

import { createSlice } from '@reduxjs/toolkit';

export const messageSlice = createSlice({
  name: 'message',
  ~~~ 上と同じ ~~~

  reducers: {
  ~~~ 上と同じ ~~~
  // この処理を追加します。
    sayAmount: (state, action) => {
      state.mess = action.payload;
    },
  },
});

// 追加したsayAmountをエクスポートできるようにする
export const { sayhello, sayAmount} = messageSlice.actions;

export default messageSlice.reducer;

storeファイルの作成

storeファイルは、上のreducerをまとめて登録しているので変更なしです。

storeをコンポーネントに適用させる

storeファイルは、上のreducerをまとめて登録しているので変更なしです。

slice経由でstoreを使用する

内容としては。ほとんど先ほどのものと変わらないです。
8行目のところでuseStateを使用して2つの関数を生成していますが、ザックリ言うとクラス内のstateの宣言などを不要にする物になります。

Message.js

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { sayhello, sayAmount } from '../store/slice/messageSlice';

export function Message() {
  const message = useSelector(state => state.message.mess);
  const dispatch = useDispatch();
  const [messsageAmount, setMesssageAmount] = useState('2');
  ~~~ 上と同じ ~~~
        {/* テキストボックスとボタンにインポートしたものを適用する  */}
        <input aria-label="set amount" value={messsageAmount} onChange={e => setMesssageAmount(e.target.value)} />
        <button onClick={() => dispatch(sayAmount(messsageAmount))}>
          テキスト変更
        </button>
      </div>
    </div>
  );
}

Reactの実行

上と同様にReactを開発用のサーバで起動してブラウザからアクセスしてください。
今回追加したテキストボックスとボタンが追加されています。テキストボックスに値を入れてボタンをクリックすると表示されているテキストが変更されます。
基本的に内容としてはほぼ表示のみと変わりません。

終わりに

axiosとの連携を書こうと考えていましたが、Reduxの説明が予想以上に長くなったので今回はここまでにします。次回以降にaxiosのサンプルと簡単な説明を書いていこうと思います。axiosのサンプルと説明はReactのRedux内でaxiosを使用した通信をするにまとめました。
今回は簡単な例なのでメリットがわからないと思いますが、Web画面や保持する情報が増えたら明確にメリットがわかるようになると思います。

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
5