ReactJS FluxにおけるStoreとActionの役割と外部サービス連携
ReactJS と ReactJS Flux における Store と Action の外部サービスとの連携
ReactJS と ReactJS Flux における Store と Action の外部サービスとの連携は、アプリケーションのデータ取得や操作を行う重要な要素です。適切な設計を行うことで、アプリケーションのモジュール性、テスト容易性、および保守性を向上させることができます。
Store と Action の役割
- Action
Store の状態を更新するためのイベントです。ユーザー操作や外部サービスからのイベントによって発生します。 - Store
アプリケーションの状態を保持するコンポーネントです。外部サービスから取得したデータや、ユーザー操作によって更新されたデータを保持します。
外部サービスとの連携
Store と Action は、以下の方法で外部サービスと連携することができます。
- Middleware を使用して外部サービスを呼び出す
Middleware を使用して、Action や Store 内での外部サービス呼び出しを制御することができます。 - Store 内で外部サービスを呼び出す
Store 内で外部サービスを呼び出し、取得したデータを Store の状態に更新することができます。
Store と Action を設計する際には、以下の点に注意する必要があります。
- Action は Store の状態を更新するためのイベントであるべきです
Action は外部サービスとの連携に関するロジックを含まず、Store の状態を更新するためのイベントであるべきです。 - Store は純粋なデータストアであるべきです
Store は外部サービスとの連携に関するロジックを含まず、データのみを保持するべきです。
例
以下の例は、ReactJS と ReactJS Flux を使用してユーザー情報を外部サービスから取得し、Store に更新するコードを示しています。
// Action
const FETCH_USER_INFO = 'FETCH_USER_INFO';
export const fetchUserInfo = (userId) => ({
type: FETCH_USER_INFO,
payload: {
userId,
},
});
// Store
class UserInfoStore {
constructor() {
this.state = {
userInfo: null,
};
}
reduce(action) {
switch (action.type) {
case FETCH_USER_INFO: {
const { userId } = action.payload;
// 外部サービスを呼び出す
fetch(`https://api.example.com/users/${userId}`)
.then((response) => response.json())
.then((userInfo) => {
this.setState({
userInfo,
});
});
break;
}
default:
return this.state;
}
}
}
// Middleware
const fetchUserInfoMiddleware = (store) => (next) => (action) => {
if (action.type === FETCH_USER_INFO) {
const { userId } = action.payload;
// 外部サービス呼び出しを制御する
if (!store.getState().userInfo) {
store.dispatch(fetchUserInfo(userId));
}
}
return next(action);
};
// アプリケーション
const store = createStore(combineReducers({
userInfo: new UserInfoStore(),
}), applyMiddleware(fetchUserInfoMiddleware));
store.dispatch(fetchUserInfo(1));
src/
├── actions/
│ └── fetchUserInfo.js
├── components/
│ └── UserInfo.js
├── middleware/
│ └── fetchUserInfoMiddleware.js
├── reducers/
│ └── userInfoReducer.js
├── store/
│ └── index.js
└── App.js
コード解説
1 actions/fetchUserInfo.js
// Action
const FETCH_USER_INFO = 'FETCH_USER_INFO';
export const fetchUserInfo = (userId) => ({
type: FETCH_USER_INFO,
payload: {
userId,
},
});
このファイルは、FETCH_USER_INFO
という名前のアクションを定義します。このアクションは、ユーザー情報の取得に使用されます。
2 components/UserInfo.js
import React from 'react';
import { useSelector } from 'react-redux';
const UserInfo = () => {
const userInfo = useSelector((state) => state.userInfo);
if (!userInfo) {
return <div>ユーザー情報を読み込み中...</div>;
}
return (
<div>
<p>名前: {userInfo.name}</p>
<p>メールアドレス: {userInfo.email}</p>
</div>
);
};
export default UserInfo;
このファイルは、UserInfo
という名前のコンポーネントを定義します。このコンポーネントは、Store からユーザー情報を読み込み、表示します。
3 middleware/fetchUserInfoMiddleware.js
import { FETCH_USER_INFO } from '../actions/fetchUserInfo';
const fetchUserInfoMiddleware = (store) => (next) => (action) => {
if (action.type === FETCH_USER_INFO) {
const { userId } = action.payload;
if (!store.getState().userInfo) {
store.dispatch(fetchUserInfo(userId));
}
}
return next(action);
};
export default fetchUserInfoMiddleware;
このファイルは、fetchUserInfoMiddleware
という名前のミドルウェアを定義します。このミドルウェアは、FETCH_USER_INFO
アクションが dispatch されたときに、Store にユーザー情報が存在しない場合は、fetchUserInfo
アクションを再度 dispatch します。
4 reducers/userInfoReducer.js
import { FETCH_USER_INFO } from '../actions/fetchUserInfo';
const initialState = {
userInfo: null,
};
export default function userInfoReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_INFO: {
return {
...state,
userInfo: action.payload.userInfo,
};
}
default:
return state;
}
}
このファイルは、userInfoReducer
という名前の reducer を定義します。この reducer は、Store の userInfo
プロパティを更新します。
5 store/index.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import fetchUserInfoMiddleware from '../middleware/fetchUserInfoMiddleware';
const store = createStore(rootReducer, applyMiddleware(fetchUserInfoMiddleware));
export default store;
このファイルは、Store を作成します。
6 App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import UserInfo from './components/UserInfo';
const App = () => (
<Provider store={store}>
<UserInfo />
</Provider>
);
export default App;
このファイルは、React アプリケーションのルートコンポーネントを定義します。
実行方法
npm install
npm start
ブラウザで http://localhost:3000
にアクセスすると、ユーザー情報が表示されます。
解説
- Store を使用してデータを保持する
userInfoReducer
は、Store のuserInfo
プロパティにユーザー情報を - Action を使用して外部サービスを呼び出す
fetchUserInfo
アクションは、外部サービスを呼び出すために使用されます。
Action 内で外部サービスを呼び出す
説明
Action 内で直接外部サービスを呼び出し、取得したデータを Store に更新する方法です。
利点
- コード量が少ない
- シンプルで分かりやすい
欠点
- 保守性が低い
- テストしにくい
- Action が複雑になりやすい
例
// Action
const FETCH_USER_INFO = 'FETCH_USER_INFO';
export const fetchUserInfo = (userId) => ({
type: FETCH_USER_INFO,
payload: {
userId,
},
});
// Store
class UserInfoStore {
constructor() {
this.state = {
userInfo: null,
};
}
reduce(action) {
switch (action.type) {
case FETCH_USER_INFO: {
const { userId } = action.payload;
// 外部サービスを呼び出す
fetch(`https://api.example.com/users/${userId}`)
.then((response) => response.json())
.then((userInfo) => {
this.setState({
userInfo,
});
});
break;
}
default:
return this.state;
}
}
}
- Action がシンプルになる
- Store が複雑になりやすい
// Store
class UserInfoStore {
constructor() {
this.state = {
userInfo: null,
};
}
fetchUserInfo(userId) {
// 外部サービスを呼び出す
fetch(`https://api.example.com/users/${userId}`)
.then((response) => response.json())
.then((userInfo) => {
this.setState({
userInfo,
});
});
}
reduce(action) {
switch (action.type) {
case FETCH_USER_INFO: {
const { userId } = action.payload;
this.fetchUserInfo(userId);
break;
}
default:
return this.state;
}
}
}
Middleware を使用して外部サービスを呼び出す
Middleware を使用して、Action や Store 内での外部サービス呼び出しを制御する方法です。
- Action と Store を独立させることができる
- コード量が増える
// Middleware
const fetchUserInfoMiddleware = (store) => (next) => (action) => {
if (action.type === FETCH_USER_INFO) {
const { userId } = action.payload;
// 外部サービス呼び出しを制御する
if (!store.getState().userInfo) {
store.dispatch(fetchUserInfo(userId));
}
}
return next(action);
};
// アプリケーション
const store = createStore(combineReducers({
userInfo: new UserInfoStore(),
}), applyMiddleware(fetchUserInfoMiddleware));
store.dispatch(fetchUserInfo(1));
どの方法が最適かは、アプリケーションの要件によって異なります。
- テストしやすく、保守性の高い方法が必要な場合は、Store 内で外部サービスを呼び出す 方法または Middleware を使用して外部サービスを呼び出す 方法がおすすめです。
- シンプルで分かりやすい方法が必要な場合は、Action 内で外部サービスを呼び出す 方法がおすすめです。
reactjs reactjs-flux