チュートリアル: connect API の使用
現在、React-Redux hooks API をデフォルトとして使用することをお勧めします。ただし、connect API も問題なく動作します。
このチュートリアルでは、Redux ロジックをタイプ別にフォルダに分けるなど、現在では推奨されていない古いプラクティスも示しています。このチュートリアルは、完全を期すために現状維持していますが、現在のベストプラクティスについては、Redux ドキュメントの「Redux Essentials」チュートリアルと、Redux スタイルガイドを参照することをお勧めします。
現在、hooks API を紹介する新しいチュートリアルを作成中です。それまでの間は、Redux Fundamentals, Part 5: UI and Reactで hooks のチュートリアルを読むことをお勧めします。
React Redux の実践的な使い方を見るために、Todo リストアプリを作成するステップバイステップの例を示します。
Todo リストの例
ジャンプ先
- 🤞 コードだけを見たい
- 👆 ストアの提供
- ✌️ コンポーネントの接続
React UI コンポーネント
React UI コンポーネントは次のように実装しました
TodoAppはアプリのエントリーコンポーネントです。ヘッダー、AddTodo、TodoList、VisibilityFiltersコンポーネントをレンダリングします。AddTodoは、ユーザーが Todo アイテムを入力し、「Todo を追加」ボタンをクリックするとリストに追加できるコンポーネントですonChangeで状態を設定する制御された入力を使用します。- ユーザーが「Todo を追加」ボタンをクリックすると、Todo をストアに追加するためのアクション (React Redux を使用して提供します) をディスパッチします。
TodoListは Todo のリストをレンダリングするコンポーネントですVisibilityFiltersのいずれかが選択されると、フィルタリングされた Todo のリストをレンダリングします。
Todoは単一の Todo アイテムをレンダリングするコンポーネントです- Todo の内容をレンダリングし、Todo が完了したことを取り消し線で示します。
onClickで Todo の完了ステータスを切り替えるアクションをディスパッチします。
VisibilityFiltersは、すべて、完了、未完了 のシンプルなフィルターセットをレンダリングします。それぞれのフィルターをクリックすると、Todo がフィルタリングされます- 親から、ユーザーが現在選択しているフィルターを示す
activeFilterプロパティを受け入れます。アクティブなフィルターは下線付きでレンダリングされます。 - 選択したフィルターを更新するために
setFilterアクションをディスパッチします。
- 親から、ユーザーが現在選択しているフィルターを示す
constantsにはアプリの定数データが保持されます。- 最後に、
indexはアプリを DOM にレンダリングします。
Redux ストア
アプリケーションの Redux 部分は、Redux ドキュメントで推奨されているパターンを使用して設定されています
- ストア
todos: 正規化された Todo のリデューサー。すべての Todo のbyIdsマップと、すべての ID のリストを含むallIdsを含みます。visibilityFilters: 単純な文字列all、completed、またはincomplete。
- アクションクリエーター
addTodoは Todo を追加するアクションを作成します。単一の文字列変数contentを取り、自己インクリメントされたidとcontentを含むpayloadでADD_TODOアクションを返しますtoggleTodoは Todo を切り替えるアクションを作成します。単一の数値変数idを取り、idのみを含むpayloadでTOGGLE_TODOアクションを返しますsetFilterはアプリのアクティブなフィルターを設定するアクションを作成します。単一の文字列変数filterを取り、filter自体を含むpayloadでSET_FILTERアクションを返します
- リデューサー
todosリデューサーADD_TODOアクションを受け取ると、allIdsフィールドにidを追加し、byIdsフィールド内に Todo を設定しますTOGGLE_TODOアクションを受け取ると、Todo のcompletedフィールドを切り替えます
visibilityFiltersリデューサーは、SET_FILTERアクションペイロードから受け取った新しいフィルターにストアのスライスを設定します
- アクションタイプ
actionTypes.jsファイルを使用して、再利用されるアクションタイプの定数を保持します
- セレクター
getTodoListはtodosストアからallIdsリストを返しますgetTodoByIdはidで指定されたストア内の Todo を検索しますgetTodosは少し複雑です。allIdsからすべてのidを取得し、byIdsで各 Todo を検索し、Todo の最終的な配列を返しますgetTodosByVisibilityFilterは可視性フィルターに従って Todo をフィルタリングします
UI コンポーネントと、上記の接続されていない Redux ストアのソースコードについては、この CodeSandbox をチェックしてください。
次に、React Redux を使用してこのストアをアプリに接続する方法を示します。
ストアの提供
まず、アプリで store を利用できるようにする必要があります。これを行うには、React Redux が提供する <Provider /> API でアプリをラップします。
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import TodoApp from './TodoApp'
import { Provider } from 'react-redux'
import store from './redux/store'
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<Provider store={store}>
<TodoApp />
</Provider>,
)
<TodoApp /> が、prop として渡された store を持つ <Provider /> でラップされていることに注意してください。

コンポーネントの接続
React Redux は、Redux ストアから値を読み取る (およびストアが更新されたときに値を再読み取りする) ための connect 関数を提供します。
connect 関数は、両方ともオプションである 2 つの引数を受け取ります
mapStateToProps: ストアの状態が変更されるたびに呼び出されます。ストアの状態全体を受け取り、このコンポーネントが必要とするデータのオブジェクトを返す必要があります。mapDispatchToProps: このパラメーターは、関数またはオブジェクトのいずれかになります。- 関数である場合、コンポーネントの作成時に一度呼び出されます。
dispatchを引数として受け取り、アクションをディスパッチするためにdispatchを使用する関数のオブジェクトを返す必要があります。 - アクションクリエーターのオブジェクトである場合、各アクションクリエーターは、呼び出されると自動的にアクションをディスパッチする prop 関数に変換されます。注: この「オブジェクトショートハンド」形式を使用することをお勧めします。
- 関数である場合、コンポーネントの作成時に一度呼び出されます。
通常、connect は次のように呼び出します
const mapStateToProps = (state, ownProps) => ({
// ... computed data from state and optionally ownProps
})
const mapDispatchToProps = {
// ... normally is an object full of action creators
}
// `connect` returns a new function that accepts the component to wrap:
const connectToStore = connect(mapStateToProps, mapDispatchToProps)
// and that function returns the connected, wrapper component:
const ConnectedComponent = connectToStore(Component)
// We normally do both in one step, like this:
connect(mapStateToProps, mapDispatchToProps)(Component)
まず、<AddTodo /> から作業を始めましょう。新しい Todo を追加するために store への変更をトリガーする必要があります。したがって、ストアにアクションを dispatch できる必要があります。これがその方法です。
addTodo アクションクリエーターは次のようになります
// redux/actions.js
import { ADD_TODO } from './actionTypes'
let nextTodoId = 0
export const addTodo = (content) => ({
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content,
},
})
// ... other actions
connect に渡すことで、コンポーネントはそれを prop として受け取り、呼び出されると自動的にアクションをディスパッチします。
// components/AddTodo.js
// ... other imports
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
class AddTodo extends React.Component {
// ... component implementation
}
export default connect(null, { addTodo })(AddTodo)
<AddTodo /> が <Connect(AddTodo) /> という親コンポーネントでラップされていることに注意してください。一方、<AddTodo /> は 1 つの prop (addTodo アクション) を取得します。

addTodo アクションをディスパッチして入力をリセットするために、handleAddTodo 関数も実装する必要があります
// components/AddTodo.js
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
class AddTodo extends React.Component {
// ...
handleAddTodo = () => {
// dispatches actions to add todo
this.props.addTodo(this.state.input)
// sets state back to empty string
this.setState({ input: '' })
}
render() {
return (
<div>
<input
onChange={(e) => this.updateInput(e.target.value)}
value={this.state.input}
/>
<button className="add-todo" onClick={this.handleAddTodo}>
Add Todo
</button>
</div>
)
}
}
export default connect(null, { addTodo })(AddTodo)
これで、<AddTodo /> がストアに接続されました。Todo を追加すると、ストアを変更するアクションがディスパッチされます。他のコンポーネントがまだ接続されていないため、アプリには表示されません。Redux DevTools Extension が接続されている場合は、アクションがディスパッチされていることがわかります

ストアもそれに応じて変更されていることがわかります

<TodoList /> コンポーネントは Todo のリストをレンダリングする役割を担っています。したがって、ストアからデータを読み取る必要があります。ストアから必要なデータの一部を記述する関数である mapStateToProps パラメーターを指定して connect を呼び出すことで、有効にします。
<Todo /> コンポーネントは、Todo アイテムを prop として受け取ります。これは todos の byIds フィールドからの情報です。ただし、どの Todo をどの順序でレンダリングする必要があるかを示すストアの allIds フィールドからの情報も必要です。mapStateToProps 関数は次のようになる可能性があります
// components/TodoList.js
// ...other imports
import { connect } from "react-redux";
const TodoList = // ... UI component implementation
const mapStateToProps = state => {
const { byIds, allIds } = state.todos || {};
const todos =
allIds && allIds.length
? allIds.map(id => (byIds ? { ...byIds[id], id } : null))
: null;
return { todos };
};
export default connect(mapStateToProps)(TodoList);
幸いなことに、これを正確に行うセレクターがあります。セレクターをインポートしてここで使用するだけです。
// redux/selectors.js
export const getTodosState = (store) => store.todos
export const getTodoList = (store) =>
getTodosState(store) ? getTodosState(store).allIds : []
export const getTodoById = (store, id) =>
getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {}
export const getTodos = (store) =>
getTodoList(store).map((id) => getTodoById(store, id))
// components/TodoList.js
// ...other imports
import { connect } from "react-redux";
import { getTodos } from "../redux/selectors";
const TodoList = // ... UI component implementation
export default connect(state => ({ todos: getTodos(state) }))(TodoList);
データの複雑なルックアップや計算は、セレクター関数にカプセル化することをお勧めします。さらに、Reselect を使用して、不要な処理をスキップできる「メモ化」されたセレクターを記述することで、パフォーマンスを最適化できます。(なぜセレクター関数を使用するのか、またどのように使用するのかについての詳細は、派生データの計算に関するReduxドキュメントと、ブログ記事Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performanceを参照してください。)
これで<TodoList />がストアに接続されました。TODOリストを受け取り、それらをマッピングして、各TODOを<Todo />コンポーネントに渡す必要があります。<Todo />はそれらを画面にレンダリングします。TODOを追加してみてください。TODOリストに表示されるはずです!

さらに多くのコンポーネントを接続します。その前に、少し立ち止まって、まずconnectについて詳しく学びましょう。
connectの一般的な呼び出し方
作業しているコンポーネントの種類に応じて、connectの呼び出し方にはさまざまな方法があり、最も一般的なものを以下にまとめます。
| ストアをサブスクライブしない | ストアをサブスクライブする | |
|---|---|---|
| アクションクリエーターを注入しない | connect()(Component) | connect(mapStateToProps)(Component) |
| アクションクリエーターを注入する | connect(null, mapDispatchToProps)(Component) | connect(mapStateToProps, mapDispatchToProps)(Component) |
ストアをサブスクライブせず、アクションクリエーターを注入しない
引数を何も指定せずにconnectを呼び出すと、コンポーネントは次のようになります。
- ストアが変更されても再レンダリングされません
- アクションを手動でディスパッチするために使用できる
props.dispatchを受け取ります
// ... Component
export default connect()(Component) // Component will receive `dispatch` (just like our <TodoList />!)
ストアをサブスクライブし、アクションクリエーターを注入しない
mapStateToPropsのみでconnectを呼び出すと、コンポーネントは次のようになります。
mapStateToPropsがストアから抽出した値にサブスクライブし、それらの値が変更された場合にのみ再レンダリングします- アクションを手動でディスパッチするために使用できる
props.dispatchを受け取ります
// ... Component
const mapStateToProps = (state) => state.partOfState
export default connect(mapStateToProps)(Component)
ストアをサブスクライブせず、アクションクリエーターを注入する
mapDispatchToPropsのみでconnectを呼び出すと、コンポーネントは次のようになります。
- ストアが変更されても再レンダリングされません
mapDispatchToPropsで注入した各アクションクリエーターをpropsとして受け取り、呼び出されると自動的にアクションをディスパッチします
import { addTodo } from './actionCreators'
// ... Component
export default connect(null, { addTodo })(Component)
ストアをサブスクライブし、アクションクリエーターを注入する
mapStateToPropsとmapDispatchToPropsの両方でconnectを呼び出すと、コンポーネントは次のようになります。
mapStateToPropsがストアから抽出した値にサブスクライブし、それらの値が変更された場合にのみ再レンダリングしますmapDispatchToPropsで注入したすべてのアクションクリエーターをpropsとして受け取り、呼び出されると自動的にアクションをディスパッチします。
import * as actionCreators from './actionCreators'
// ... Component
const mapStateToProps = (state) => state.partOfState
export default connect(mapStateToProps, actionCreators)(Component)
これら4つのケースが、connectの最も基本的な使用法を網羅しています。connectの詳細については、より詳細に説明しているAPIセクションを引き続きお読みください。
それでは、残りの<TodoApp />を接続しましょう。
TODOの切り替えの相互作用はどのように実装すればよいでしょうか?熱心な読者ならすでにお答えが出ているかもしれません。環境をセットアップし、ここまでの手順に従ってきた場合は、ここで一度手を止めて、自分で機能を実装してみるのも良いでしょう。<Todo />を同様の方法で接続してtoggleTodoをディスパッチしても驚くことではないでしょう。
// components/Todo.js
// ... other imports
import { connect } from "react-redux";
import { toggleTodo } from "../redux/actions";
const Todo = // ... component implementation
export default connect(
null,
{ toggleTodo }
)(Todo);
これでTODOの完了を切り替えることができます。もうすぐです!

最後に、VisibilityFilters機能を実装しましょう。
<VisibilityFilters />コンポーネントは、現在アクティブなフィルターをストアから読み取り、ストアにアクションをディスパッチできる必要があります。したがって、mapStateToPropsとmapDispatchToPropsの両方を渡す必要があります。ここのmapStateToPropsは、visibilityFilter状態の単純なアクセサーにすることができます。また、mapDispatchToPropsには、setFilterアクションクリエーターが含まれます。
// components/VisibilityFilters.js
// ... other imports
import { connect } from "react-redux";
import { setFilter } from "../redux/actions";
const VisibilityFilters = // ... component implementation
const mapStateToProps = state => {
return { activeFilter: state.visibilityFilter };
};
export default connect(
mapStateToProps,
{ setFilter }
)(VisibilityFilters);
一方、アクティブなフィルターに従ってTODOをフィルタリングするために、<TodoList />コンポーネントも更新する必要があります。以前、<TodoList />のconnect関数呼び出しに渡したmapStateToPropsは、単にTODOリスト全体を選択するセレクターでした。TODOをステータスでフィルタリングするのに役立つ別のセレクターを記述しましょう。
// redux/selectors.js
// ... other selectors
export const getTodosByVisibilityFilter = (store, visibilityFilter) => {
const allTodos = getTodos(store)
switch (visibilityFilter) {
case VISIBILITY_FILTERS.COMPLETED:
return allTodos.filter((todo) => todo.completed)
case VISIBILITY_FILTERS.INCOMPLETE:
return allTodos.filter((todo) => !todo.completed)
case VISIBILITY_FILTERS.ALL:
default:
return allTodos
}
}
そして、セレクターの助けを借りてストアに接続します
// components/TodoList.js
// ...
const mapStateToProps = (state) => {
const { visibilityFilter } = state
const todos = getTodosByVisibilityFilter(state, visibilityFilter)
return { todos }
}
export default connect(mapStateToProps)(TodoList)
これで、React Reduxを使用したTODOアプリの非常に簡単な例が完成しました。すべてのコンポーネントが接続されました!素晴らしいでしょう?🎉🎊

リンク
さらにヘルプが必要な場合
- Reactiflux Reduxチャンネル
- StackOverflow
- GitHub Issues