Reduxはなぜ必要?(TypeScriptサンプルコード付)
はじめに
これからReactNativeエンジニアとして働くことになり、初めてRedux
の触れることになりました。
そのため、TypeScriptのサンプルコードを実行しながら、概念から実装までを体系的に学んでいこうと思います。
実際にコードを書きながら、Redux
の仕組みの活用方法を深く理解してみましょう!
Reduxとは?
React
のコンポーネントたちの状態のロジックやグローバル状態管理ができるライブラリです。
React
でコンポーネント基盤開発をする時、親コンポーネントに対して子コンポーネントが多くなるほどprops
の伝達も増えることで、全コンポーネントの再レンダーリングが発生してしまい、パフォーマンスの低下につながる可能性が高くなります。
このような問題点を解消するためにRedux
を利用すると、グローバル状態管理をすると管理しやすくなります。
Propsで状態管理(サンプルコード)
最上位のコンポーネント
// App.tsx - propsを渡す非効率な方法
import React, { useState } from "react";
import UserProfile from "./components/UserProfile";
interface User {
id: number;
name: string;
age: number;
email: string;
address: string;
phone: string;
}
const App: React.FC = () => {
const [user, setUser] = useState<User>({
id: 1,
name: "Jiyeon",
age: 25,
email: "Jiyeon@example.com",
address: "Tokyo, Japan",
phone: "090-1234-5678",
});
const updateUser = (newUserData: Partial<User>) => {
setUser((prevUser) => ({ ...prevUser, ...newUserData }));
};
return (
<div>
<h1>ユーザー情報</h1>
{/* propsを複数のコンポーネントに渡さなければならない */}
<UserProfile user={user} updateUser={updateUser} />
</div>
);
};
export default App;
中間コンポーネント
// components/UserProfile.tsx - propsを受け取り、さらに下位コンポーネントに渡す
import React from "react";
import UserDetail from "./UserDetail";
interface User {
id: number;
name: string;
age: number;
email: string;
address: string;
phone: string;
}
interface UserProfileProps {
user: User;
updateUser: (newUserData: Partial<User>) => void;
}
const UserProfile: React.FC<UserProfileProps> = ({ user, updateUser }) => {
return (
<div>
<h2>プロフィール</h2>
{/* propsをそのままUserDetailコンポーネントに渡す */}
<UserDetail user={user} updateUser={updateUser} />
</div>
);
};
export default UserProfile;
最下層のコンポーネント
// components/UserDetail.tsx - 最終的にpropsを使用
import React from "react";
interface User {
id: number;
name: string;
age: number;
email: string;
address: string;
phone: string;
}
interface UserDetailProps {
user: User;
updateUser: (newUserData: Partial<User>) => void;
}
const UserDetail: React.FC<UserDetailProps> = ({ user, updateUser }) => {
const changeUserName = () => {
updateUser({ name: "Updated Name" });
};
return (
<div>
<p>名前: {user.name}</p>
<p>年齢: {user.age}</p>
<p>メール: {user.email}</p>
<p>住所: {user.address}</p>
<p>電話番号: {user.phone}</p>
<button onClick={changeUserName} className="mt-2 p-2 bg-blue-500 text-white">
名前を変更
</button>
</div>
);
};
export default UserDetail;
Props方式の問題点
props
を追加する必要がある
すべての親コンポーネントで - 新しいコンポーネントが追加されるたびに、最上位のコンポーネント(
App.tsx
)からすべての親コンポーネントを修正する必要がある - コンポーネント間の結合度が高くなり、メンテナンスも難しくなりやすい
props
の受け渡しが増えるとコードが複雑になる
-
props
の受け渡しが多くなると、コードが長くなり、可読性が低下する
新しい状態フィールドを追加すると、すべてのコンポーネントを修正する必要がある
- 例えば、
user
オブジェクトにnickname
フィールドを追加するとしたら**App.tsx
、UserProfile.tsx
、UserDetail.tsx
のすべてを修正する必要がある**
🔥Reduxを使って状態管理を簡単にしてみよう
上記のprops
を使った状態管理のサンプルコードをRedux Toolkit
を使ってリファクタリングしてみましょう。
Redux
を使うことでprops
の受け渡しをなくし、どのコンポーネントからでも状態を簡単に取得できるようになります。
reduxの仕組み
イメージソース:freecodecamp
Redux は 以下の 5 つの要素 で構成されています。
要素 | 説明 |
---|---|
View/UI | ユーザーが操作する画面 (Reactコンポーネント) |
Action | 状態を変更するための命令 (イベントの発生) |
Reducer |
Action に基づいて State を変更する関数 |
Store | アプリケーション全体の状態を保持するオブジェクト |
Dispatch |
Action を Reducer に送る関数 |
Reduxのデータフロー
- ユーザーが UI で操作する (ボタンを押すなど)
dispatch(Action)
を発行し、アクションを発生させるReducer
がAction
の内容を元にState
を変更する- 変更後の
State
はStore
に保存される Store
の変更をsubscribe
しているView/UI
が検知し、画面が更新される
これにより、アプリケーション全体の状態が予測可能になり、管理しやすくなリます。
上記のフローを覚えながらコードをリファクタリングしてみましょう。
Reduxで状態管理(サンプルコード)
Redux のストアを作成
まず、createSlice
を使ってReducer
とAction
をまとめて作成します。
// store/userSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
// ユーザー情報の型定義
interface User {
id: number;
name: string;
age: number;
email: string;
address: string;
phone: string;
}
// 初期状態
const initialState: User = {
id: 1,
name: "Jiyeon",
age: 25,
email: "Jiyeon@example.com",
address: "Tokyo, Japan",
phone: "090-1234-5678",
};
// Redux Slice の作成
const userSlice = createSlice({
name: "user",
initialState,
reducers: {
updateUser: (state, action: PayloadAction<Partial<User>>) => {
return { ...state, ...action.payload };
},
},
});
// アクションとリデューサーのエクスポート
export const { updateUser } = userSlice.actions;
export default userSlice.reducer;
Redux ストアのセットアップ
configureStore
を使ってRedux
ストアを作成します。
// store/index.ts
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
export const store = configureStore({
reducer: {
user: userReducer,
},
});
// RootState と Dispatch の型定義
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Provider で Redux を適用
Redux
のProvider
でアプリ全体をラップします。これで全てのコンポーネントからRedux
の状態を使用できるようになります!
// _app.tsx
import { Provider } from "react-redux";
import { store } from "../store";
function MyApp({ Component, pageProps }: any) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
export default MyApp;
propsの受け渡しなしでReduxの状態を取得
props
の受け渡しを完全に削除しました!子コンポーネントはRedux
から直接状態を取得できます。
親コンポーネント
// App.tsx - propsを渡さずにReduxの状態を直接使用
import React from "react";
import UserProfile from "./components/UserProfile";
import UserDetail from "./components/UserDetail";
const App: React.FC = () => {
return (
<div>
<h1>ユーザー情報</h1>
<UserProfile />
<UserDetail />
</div>
);
};
export default App;
子コンポーネント1
// components/UserProfile.tsx - Redux から直接状態を取得
import React from "react";
import { useSelector } from "react-redux";
import { RootState } from "../store";
const UserProfile: React.FC = () => {
const user = useSelector((state: RootState) => state.user);
return (
<div>
<h2>プロフィール</h2>
<p>名前: {user.name}</p>
<p>年齢: {user.age}</p>
<p>メール: {user.email}</p>
</div>
);
};
export default UserProfile;
子コンポーネント2
// components/UserDetail.tsx - Redux の状態を直接使用し、更新も可能
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { RootState, updateUser } from "../store/userSlice";
const UserDetail: React.FC = () => {
const user = useSelector((state: RootState) => state.user);
const dispatch = useDispatch();
const changeUserName = () => {
dispatch(updateUser({ name: "Updated Name" }));
};
return (
<div>
<p>名前: {user.name}</p>
<p>年齢: {user.age}</p>
<p>メール: {user.email}</p>
<p>住所: {user.address}</p>
<p>電話番号: {user.phone}</p>
<button onClick={changeUserName} className="mt-2 p-2 bg-blue-500 text-white">
名前を変更
</button>
</div>
);
};
export default UserDetail;
Redux
を使うことで、props
の受け渡しなしで、どのコンポーネントからでもState
を直接取得できるようになります!
まとめ
Redux を導入することで、以下のメリットがあります。
-
props
の受け渡しを削減し、コードの可読性を向上 - アプリケーションの状態をグローバルに管理し、状態管理を簡潔に
- どのコンポーネントからでも
useSelector
で状態を取得可能 -
useDispatch
を利用して、状態を簡単に更新
Redux
を活用することで、Reactアプリの状態管理をスムーズにし、スケーラブルな開発が可能になりますので活用してみましょう 🚀
Discussion