TypeScript で Redux アクションとリデューサーを型指定するその他の方法
TypeScript で Redux アクションと Redux リデューサーを型指定する方法
アクションの型指定
Redux アクションの型指定には、いくつかの方法があります。
列挙型を使用する
最も単純な方法は、列挙型を使用してアクションの型を定義することです。
enum ActionTypes {
ADD_TODO = 'ADD_TODO',
TOGGLE_TODO = 'TOGGLE_TODO',
DELETE_TODO = 'DELETE_TODO',
}
上記のように、各アクションの型に名前を付けることができます。
アクション クリエーターは、対応する列挙型メンバーを使用してアクションを作成できます。
export const addTodo = (text: string): Action => ({
type: ActionTypes.ADD_TODO,
payload: text,
});
インターフェースを使用する
より複雑なアクションの場合は、インターフェースを使用して型を定義することができます。
interface AddTodoAction {
type: ActionTypes.ADD_TODO;
payload: string;
}
interface ToggleTodoAction {
type: ActionTypes.TOGGLE_TODO;
id: number;
}
interface DeleteTodoAction {
type: ActionTypes.DELETE_TODO;
id: number;
}
上記のように、各アクションの型にプロパティとその型を定義することができます。
export const addTodo = (text: string): AddTodoAction => ({
type: ActionTypes.ADD_TODO,
payload: text,
});
export const toggleTodo = (id: number): ToggleTodoAction => ({
type: ActionTypes.TOGGLE_TODO,
id,
});
export const deleteTodo = (id: number): DeleteTodoAction => ({
type: ActionTypes.DELETE_TODO,
id,
});
リデューサーの型指定
Redux リデューサーの型指定には、createReducer
関数を使用することができます。
import { createReducer } from '@reduxjs/toolkit';
const initialState: TodoState = {
todos: [],
};
const todoReducer = createReducer(initialState, {
[ActionTypes.ADD_TODO]: (state, action: AddTodoAction) => {
state.todos.push({
id: state.todos.length,
text: action.payload,
completed: false,
});
},
[ActionTypes.TOGGLE_TODO]: (state, action: ToggleTodoAction) => {
const todo = state.todos.find((todo) => todo.id === action.id);
if (todo) {
todo.completed = !todo.completed;
}
},
[ActionTypes.DELETE_TODO]: (state, action: DeleteTodoAction) => {
state.todos = state.todos.filter((todo) => todo.id !== action.id);
},
});
上記のように、createReducer
関数に初期ステートとアクションハンドラーのオブジェクトを渡すことで、型指定されたリデューサーを作成することができます。
アクションハンドラーは、対応するアクションの型に基づいて引数を受け取るように型指定されます。
利点
- IDE のサポートを活用することができます。
- コードの可読性と保守性を向上させることができます。
- 型指定を行うことで、アクションとリデューサーの構造を明確にし、予期せぬエラーを防ぐことができます。
ファイル構成
src
├── actions.ts
├── reducers.ts
└── store.ts
actions.ts
enum ActionTypes {
ADD_TODO = 'ADD_TODO',
TOGGLE_TODO = 'TOGGLE_TODO',
DELETE_TODO = 'DELETE_TODO',
}
interface AddTodoAction {
type: ActionTypes.ADD_TODO;
payload: string;
}
interface ToggleTodoAction {
type: ActionTypes.TOGGLE_TODO;
id: number;
}
interface DeleteTodoAction {
type: ActionTypes.DELETE_TODO;
id: number;
}
export const addTodo = (text: string): AddTodoAction => ({
type: ActionTypes.ADD_TODO,
payload: text,
});
export const toggleTodo = (id: number): ToggleTodoAction => ({
type: ActionTypes.TOGGLE_TODO,
id,
});
export const deleteTodo = (id: number): DeleteTodoAction => ({
type: ActionTypes.DELETE_TODO,
id,
});
reducers.ts
import { createReducer } from '@reduxjs/toolkit';
export interface Todo {
id: number;
text: string;
completed: boolean;
}
export interface TodoState {
todos: Todo[];
}
const initialState: TodoState = {
todos: [],
};
export const todoReducer = createReducer(initialState, {
[ActionTypes.ADD_TODO]: (state, action: AddTodoAction) => {
state.todos.push({
id: state.todos.length,
text: action.payload,
completed: false,
});
},
[ActionTypes.TOGGLE_TODO]: (state, action: ToggleTodoAction) => {
const todo = state.todos.find((todo) => todo.id === action.id);
if (todo) {
todo.completed = !todo.completed;
}
},
[ActionTypes.DELETE_TODO]: (state, action: DeleteTodoAction) => {
state.todos = state.todos.filter((todo) => todo.id !== action.id);
},
});
store.ts
import { createStore } from '@reduxjs/toolkit';
import { todoReducer } from './reducers';
export const store = createStore(todoReducer);
使い方
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
const Root = () => (
<Provider store={store}>
<App />
</Provider>
);
export default Root;
このコードは、以下の機能を提供します。
store.ts
:ストアを作成します。reducers.ts
:リデューサーを定義します。actions.ts
:アクションを定義します。
@reduxjs/toolkit を使用する
@reduxjs/toolkit
は、Redux をより使いやすくするためのライブラリです。@reduxjs/toolkit
には、アクションとリデューサーを型指定するためのユーティリティが含まれています。
例
import { configureStore } from '@reduxjs/toolkit';
const todoReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.payload);
default:
return state;
}
};
const store = configureStore({ reducer: todoReducer });
上記のように、createStore
関数を使用してストアを作成し、reducer
オプションに型指定されたリデューサーを渡すことができます。
redux-actions を使用する
redux-actions
は、Redux アクションを型指定するためのライブラリです。
import { createAction } from 'redux-actions';
const addTodo = createAction('ADD_TODO', (text) => ({
type: 'ADD_TODO',
payload: text,
}));
const toggleTodo = createAction('TOGGLE_TODO', (id) => ({
type: 'TOGGLE_TODO',
id,
}));
const deleteTodo = createAction('DELETE_TODO', (id) => ({
type: 'DELETE_TODO',
id,
}));
const todoReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.payload);
default:
return state;
}
};
上記のように、createAction
関数を使用してアクションを作成することができます。createAction
関数は、アクションの型とペイロードの型を指定することができます。
手動で型指定を行う
上記の方法を使用せずに、手動で型指定を行うこともできます。
type ActionTypes = {
ADD_TODO: { type: 'ADD_TODO'; payload: string };
TOGGLE_TODO: { type: 'TOGGLE_TODO'; payload: number };
DELETE_TODO: { type: 'DELETE_TODO'; payload: number };
};
const addTodo = (text: string): ActionTypes.ADD_TODO => ({
type: 'ADD_TODO',
payload: text,
});
const toggleTodo = (id: number): ActionTypes.TOGGLE_TODO => ({
type: 'TOGGLE_TODO',
id,
});
const deleteTodo = (id: number): ActionTypes.DELETE_TODO => ({
type: 'DELETE_TODO',
id,
});
const todoReducer = (state = [], action: ActionTypes): TodoState => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.payload);
default:
return state;
}
};
上記のように、アクションとリデューサーの型を手動で定義することができます。
どの方法を選択すべきか
どの方法を選択するかは、開発者の好みやプロジェクトの要件によって異なります。
- **完全な型制御が必要な場合は、手動で型指定を行う
- アクションの型をより細かく制御したい場合は、
redux-actions
を使用するのがおすすめです。 - シンプルさを重視する場合は、
@reduxjs/toolkit
を使用するのがおすすめです。
typescript redux