もう今後はRecoilかな。。。とは思いますが、Reduxも他人のソース見たりするときに必要なので書いておきます。
Recoil版はこちら。
やりたいこと
- hook利用下でのRedux利用
- react-router-domを利用したページ遷移での値の維持確認
- データの永続化
下記のような感じ。
準備
まず準備。
作業場の確保とライブラリのインストール
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を記述。
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を記述。
import { Link } from "react-router-dom";
const About = () => {
return (
<>
<h1>About</h1>
<Link to="/">Home</Link>
</>
);
}
export default About;
作成したHome.jsとAbout.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を利用して、このページ間で状態を維持するようにしてみたいと思います。
Reduxを利用した実装
ここからが本題です。
Reducerを作っておく
src以下にreducersというディレクトリを作成し、その下にrootReducer.jsを作成します。
初期値は変数とオブジェクトを用意。countとuser.ageをカウントアップするactionを用意。
//初期値
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に記述します。
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しなくていいのはいいですね。
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。とりあえず表示だけ。
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の作成
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対応できるコードに変更。
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は永続化されていないことが確認できました。