LoginSignup
1
1

More than 1 year has passed since last update.

Redux最低限(2021年8月)

Last updated at Posted at 2021-08-31

もう今後はRecoilかな。。。とは思いますが、Reduxも他人のソース見たりするときに必要なので書いておきます。
Recoil版はこちら

やりたいこと

  • hook利用下でのRedux利用
  • react-router-domを利用したページ遷移での値の維持確認
  • データの永続化

下記のような感じ。

スクリーンショット 2021-08-31 19.58.35.png

準備

まず準備。

作業場の確保とライブラリのインストール

npx create-react-app redux-test
cd redux-test

npm install redux react-redux react-router-dom

Reduxとは関係のない実装(骨組みづくり)

まず、複数ページからなる最低限の構造を作ります。とりあえずHomeとAboutからなるサイトにしてみます。

src以下にHome.jsとAbout.jsを作ります。

touch src/Home.js src/About

Home.jsを記述。

Home.js
import { Link } from "react-router-dom";

const Home = () => {
    return (
        <>
            <h1>Home</h1>
            <Link to="/about">About</Link>
        </>
    );
}

export default Home;

移動には<a></a>ではなく<Link></Lin>を利用してください。じゃないと、ページが更新されrecoilによる値の維持ができません。

About.jsを記述。

About.js
import { Link } from "react-router-dom";

const About = () => {
    return (
        <>
            <h1>About</h1>
            <Link to="/">Home</Link>
        </>
    );
}

export default About;

作成したHome.jsとAbout.jsを読み込んでサイトの構造を作ります。

App.js
import { BrowserRouter, Switch, Route } from "react-router-dom";

import Home from "./Home";
import About from "./About";

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/" exact component={Home} />
        <Route path="/about" exact component={About} />
        <Route render={() => <p>Page not found.</p>} />
      </Switch>
    </BrowserRouter>
  );
}
export default App;

一度、動作を確認して置きましょう。

npm start

ページ間を移動する簡単なサイトができました。
Recoilを利用して、このページ間で状態を維持するようにしてみたいと思います。

スクリーンショット 2021-08-31 6.51.59.png

Reduxを利用した実装

ここからが本題です。

Reducerを作っておく

src以下にreducersというディレクトリを作成し、その下にrootReducer.jsを作成します。
初期値は変数とオブジェクトを用意。countとuser.ageをカウントアップするactionを用意。

src/reducers/rootReducer.js
//初期値
const initialState = {
    count: 0,
    user: {
        name: "hoge",
        age: 11
    }
}

//reducer
export const reducer = (state = initialState, action) => {
    switch (action.type) {
        case "count_increment":
            return { ...state, count: state.count + 1 };
        case "age_increment":
            //deep copy
            const newState = JSON.parse(JSON.stringify(state));
            newState.user.age = state.user.age + 1;
            return newState;
        default:
            return state;
    }
}

Reduxで状態を共有したエリアを囲む

RecoilでもContextでも値を共有したいエリアを専用タグで囲みました。ReduxではProviderです。
多くの場合、サイト全体で値を共有するのでindex.jsに記述します。

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

//redux関連読み込み
import { createStore } from "redux";
import { Provider } from "react-redux";

//reducer読み込み
import { reducer } from "./reducers/rootReducer";

//グローバルでの値保管場所生成
const store = createStore(reducer);

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

reportWebVitals();

Home.jsの編集(値の利用と更新)

Homeにstate値の表示と編集用の関数を設置。
値はuseSelector()で、関数はdispatchで利用する。connectしなくていいのはいいですね。

Home.js
import { useSelector, useDispatch } from "react-redux";
import { Link } from "react-router-dom";

const Home = () => {

    //state(値)の利用
    const count = useSelector(state => state.count);
    const user = useSelector(state => state.user);
    //dispatch(値編集関数)の利用
    const dispatch = useDispatch();

    //counterを加算
    const incrementCount = () => {
        dispatch({ type: "count_increment" });
    }

    //ageを加算
    const incrementAge = () => {
        dispatch({ type: "age_increment" });
    }

    return (
        <>
            <h1>Home</h1>
            <p>count:{count}</p>
            <button onClick={() => incrementCount()}>increment</button>
            <hr />
            <p>user.name:{user.name}</p>
            <p>user.name:{user.age}</p>
            <button onClick={() => incrementAge()}>increment user age</button>
            <br/>
            <br/>
            <Link to="/about">About</Link>
        </>
    );
}

export default Home;

About.js

続いてAbout。とりあえず表示だけ。

About.js
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";

const About = () => {

    //state(値)の利用
    const count = useSelector(state => state.count);
    const user = useSelector(state => state.user);

    return (
        <>
            <h1>About</h1>
            <p>count:{count}</p>
            <hr />
            <p>user.name:{user.name}</p>
            <p>user.name:{user.age}</p>
            <br/>
            <br/>
            <Link to="/">Home</Link>
        </>
    );
}

export default About;

値の永続化(Persist)

実用的なアプリを開発するにはデータの永続化は不可欠ですのでやり方を検証しておきます。

モジュールの追加

あまりメンテされてるモジュールがないみたい。とりえあえずredux-persist使う。

npm install redux-persist

redux-persistのページで紹介されている例を参考にconfigureStore.jsを作成。
ここで単純なstoreではなく、persist対応のstoreを作成。userだけ永続化する(countはしない)。

configureStore.jsの作成

src/configureStore.js
import { createStore } from 'redux'
import { persistReducer, persistStore } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

//reducer読み込み
import { reducer } from "./reducers/rootReducer";

//config
const persistConfig = {
    key: 'root',
    storage,
    whitelist: ['user'] //whitelist この場合、userのみ永続化される(countはされない)
}

const persistedReducer = persistReducer(persistConfig, reducer);

const store = createStore(
    persistedReducer,
);

export const persistor = persistStore(store)
export default store

index.jsの編集

index.jsを変種してpersist対応できるコードに変更。

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

//redux
import { Provider } from "react-redux";

//persist
import { PersistGate } from 'redux-persist/integration/react'
//configureStoreの読み込み
import store, { persistor } from './configureStore'

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

動作確認してみると、user.ageの値は残るが、countは永続化されていないことが確認できました。

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