メインコンテンツへスキップ

Redux Fundamentals, Part 5: UI and React

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

学ぶ内容
  • ReduxストアとUIの連携方法
  • ReactとReduxの使用方法

はじめに

Part 4: Storeでは、Reduxストアの作成方法、アクションのディスパッチ方法、現在の状態の読み取り方法を学びました。また、ストアの内部動作、エンハンサーとミドルウェアによる追加機能のカスタマイズ方法、Redux DevToolsの追加によるアクション発行時のアプリ内部の可視化方法も見てきました。

このセクションでは、Todoアプリにユーザーインターフェースを追加します。ReduxとUIレイヤーの全体的な連携方法を学び、特にReactとの連携方法について詳しく説明します。

注意

このページおよび「Essentials」チュートリアル全体では、最新のReact-ReduxフックAPIの使用方法を解説します。旧来のconnect APIも動作しますが、現在では全てのReduxユーザーにフックAPIの使用を推奨しています。

また、本チュートリアルの他のページでは、Reduxの原理と概念を説明するため、現代的なRedux Toolkitを使用した「モダンRedux」パターンよりも多くのコードを必要とする旧来のReduxロジックパターンを意図的に示しています。

実際のアプリケーション向けの「正しいReduxの使用方法」であるRedux ToolkitとReact-Reduxフックの完全なサンプルについては、「Redux Essentials」チュートリアルを参照してください。

ReduxとUIの統合

ReduxはスタンドアロンのJavaScriptライブラリです。これまで見てきたように、ユーザーインターフェースを設定しなくてもReduxストアを作成して使用できます。これはつまり、任意のUIフレームワーク(あるいはUIフレームワークを全く使用せずに)でReduxを使用でき、クライアント側でもサーバー側でも利用できることを意味します。React、Vue、Angular、Ember、jQuery、あるいはバニラJavaScriptでReduxアプリを開発できます。

ただし、Reduxは特にReactとの連携を考慮して設計されています。ReactではUIを状態の関数として記述し、Reduxは状態を保持してアクションに応じて更新します。

そのため、このチュートリアルではTodoアプリを構築する際にReactを使用し、ReactとReduxの基本的な連携方法を説明します。

その前に、ReduxがUIレイヤーとどのように相互作用するかを簡単に見てみましょう。

ReduxとUIの基本的な統合

任意のUIレイヤーでReduxを使用するには、次の一貫した手順が必要です:

  1. Reduxストアを作成する

  2. 更新をサブスクライブする

  3. サブスクリプションコールバック内で:

    1. 現在のストアの状態を取得する
    2. このUI部分に必要なデータを抽出する
    3. データでUIを更新する
  4. 必要に応じて初期状態でUIをレンダリングする

  5. UI入力に応じてReduxアクションをディスパッチする

Part 1で見たカウンターアプリの例に戻り、これらの手順がどのように適用されているか見てみましょう:

// 1) Create a new Redux store with the `createStore` function
const store = Redux.createStore(counterReducer)

// 2) Subscribe to redraw whenever the data changes in the future
store.subscribe(render)

// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')

// 3) When the subscription callback runs:
function render() {
// 3.1) Get the current store state
const state = store.getState()
// 3.2) Extract the data you want
const newValue = state.value.toString()

// 3.3) Update the UI with the new value
valueEl.innerHTML = newValue
}

// 4) Display the UI with the initial store state
render()

// 5) Dispatch actions based on UI inputs
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})

使用するUIレイヤーに関わらず、ReduxはすべてのUIで同じように動作します。実際の実装ではパフォーマンス最適化のため若干複雑になりますが、手順は毎回同じです。

Reduxは独立したライブラリであるため、特定のUIフレームワークでReduxを使用するためのさまざまな「バインディング」ライブラリが存在します。これらのUIバインディングライブラリは、ストアのサブスクライブや状態変化時のUI効率的な更新といった詳細を処理するため、自分でそのようなコードを書く必要がありません。

ReactとReduxの使用

公式の React-Redux UIバインディングライブラリ はReduxコアとは別パッケージです。追加でインストールする必要があります:

npm install react-redux

このチュートリアルでは、ReactとReduxを組み合わせて使用する際に必要な最も重要なパターンと例をカバーし、TODOアプリの一部として実際にどのように機能するかを見ていきます。

情報

ReactとReduxを組み合わせて使用する方法の完全ガイドおよびReact-Redux APIのリファレンスについては、公式React-Reduxドキュメント https://react-redux.js.org を参照してください

コンポーネントツリーの設計

以前に要件に基づいて状態構造を設計したのと同様に、アプリケーション内のUIコンポーネントの全体像とそれらの相互関係も設計できます。

アプリのビジネス要件リストに基づくと、少なくとも以下のコンポーネントが必要です:

  • <App>: 他のすべてをレンダリングするルートコンポーネント
    • <Header>: 「新しいTODO」テキスト入力と「すべてのTODOを完了」チェックボックスを含む
    • <TodoList>: フィルタリング結果に基づく現在表示可能な全TODOアイテムのリスト
      • <TodoListItem>: 単一のTODOリストアイテム。TODOの完了状態を切り替えるチェックボックスとカラーカテゴリセレクターを含む
    • <Footer>: アクティブなTODOの数を表示し、完了状態とカラーカテゴリに基づくリストフィルタリングコントロールを提供

この基本構造を超えて、コンポーネントをいくつかの方法で分割できます。例えば、<Footer>コンポーネントは単一の大きなコンポーネントにすることも、<CompletedTodos><StatusFilter><ColorFilters>などの小さなコンポーネントに分割することも可能です。状況に応じて、大きなコンポーネントを書くか複数の小さなコンポーネントに分割するかはケースバイケースです。

今のところ、理解しやすくするためにこの小規模なコンポーネントリストから始めます。なお、Reactの知識は既にあると仮定しているため、これらのコンポーネントのレイアウトコードの詳細は飛ばし、Reactコンポーネントで実際にReact-Reduxライブラリを使用する方法に焦点を当てます

以下は、Redux関連ロジックを追加する前のアプリの初期React UIです:

useSelectorでストアから状態を読み取る

TODOアイテムのリストを表示できるようにする必要があります。まず、ストアからTODOリストを読み取り、それらをループ処理して各TODOエントリに対して1つの<TodoListItem>コンポーネントを表示する<TodoList>コンポーネントを作成しましょう。

useStateのようなReactフックには馴染みがあるでしょう。これらはReact関数コンポーネント内で呼び出してReactの状態値にアクセスできます。Reactではカスタムフックも作成でき、組み込みフックの上に独自の動作を追加する再利用可能なフックを抽出できます。

他の多くのライブラリと同様に、React-Reduxにも独自のカスタムフックが含まれており、コンポーネント内で使用できます。React-Reduxフックを使用すると、Reactコンポーネントが状態を読み取り、アクションをディスパッチすることでReduxストアと通信できるようになります。

最初に見るReact-ReduxフックはuseSelectorフックです。これはReactコンポーネントがReduxストアからデータを読み取ることを可能にします。

useSelectorは単一の関数(セレクタ関数)を受け取ります。セレクタはReduxストアの状態全体を引数として受け取り、状態から値を読み取ってその結果を返す関数です。

例えば、TodoアプリのRedux状態はTodoアイテムの配列をstate.todosとして保持しています。このtodos配列を返す小さなセレクタ関数を書くことができます:

const selectTodos = state => state.todos

あるいは、現在「完了」としてマークされているTodoの数を取得したい場合もあるでしょう:

const selectTotalCompletedTodos = state => {
const completedTodos = state.todos.filter(todo => todo.completed)
return completedTodos.length
}

このように、セレクタはReduxストアの状態から値を返すだけでなく、その状態に基づいた派生値も返すことができます

<TodoList>コンポーネントにtodos配列を読み込みましょう。まず、react-reduxライブラリからuseSelectorフックをインポートし、引数としてセレクタ関数を渡します:

src/features/todos/TodoList.js
import React from 'react'
import { useSelector } from 'react-redux'
import TodoListItem from './TodoListItem'

const selectTodos = state => state.todos

const TodoList = () => {
const todos = useSelector(selectTodos)

// since `todos` is an array, we can loop over it
const renderedListItems = todos.map(todo => {
return <TodoListItem key={todo.id} todo={todo} />
})

return <ul className="todo-list">{renderedListItems}</ul>
}

export default TodoList

<TodoList>コンポーネントが初めてレンダリングされるとき、useSelectorフックはselectTodosを呼び出し、Reduxの状態オブジェクト全体を渡します。セレクタが返す値は、フックからコンポーネントに返されます。したがって、コンポーネント内のconst todosは、Reduxストア状態内のstate.todos配列と同じものを保持することになります。

しかし、{type: 'todos/todoAdded'}のようなアクションをディスパッチするとどうなるでしょうか?ReducerによってRedux状態は更新されますが、コンポーネントは新しいtodosリストで再レンダリングするために、何か変更があったことを知る必要があります。

ストアの変更をリッスンするためにstore.subscribe()を呼び出せることは知っていますが、すべてのコンポーネントでストアにサブスクライブするコードを書こうとすると、すぐに繰り返しが多くなり扱いにくくなります。

幸いなことに、useSelectorは自動的にReduxストアにサブスクライブしてくれます! これにより、アクションがディスパッチされるたびに、すぐにセレクタ関数が再実行されます。セレクタが返す値が前回の実行時から変化した場合、useSelectorは新しいデータでコンポーネントを強制的に再レンダリングさせます。コンポーネント内で一度useSelector()を呼び出すだけで、残りの作業はすべて処理してくれます。

ただし、ここで非常に重要な注意点があります:

注意

useSelectorは結果を厳密な===参照比較でチェックするため、セレクタの結果が新しい参照になるたびにコンポーネントが再レンダリングされます! つまり、セレクタ内で新しい参照を作成して返すと、データが実際に変化していなくても、アクションがディスパッチされるたびにコンポーネントが再レンダリングされる可能性があります。

例えば、このセレクタをuseSelectorに渡すと、コンポーネントは常に再レンダリングされます。なぜならarray.map()は常に新しい配列参照を返すからです:

// Bad: always returning a new reference
const selectTodoDescriptions = state => {
// This creates a new array reference!
return state.todos.map(todo => todo.text)
}
ヒント

この問題を解決する方法については、このセクションの後半で説明します。また、パート7:標準Reduxパターンでは、「メモ化」されたセレクタ関数を使用してパフォーマンスを向上させ、不要な再レンダリングを回避する方法についても説明します。

セレクタ関数を別の変数として書く必要はないことも注目に値します。useSelectorの呼び出し内に直接セレクタ関数を書くこともできます:

const todos = useSelector(state => state.todos)

useDispatchでアクションをディスパッチする

これで、Reduxストアからコンポーネントにデータを読み込む方法がわかりました。では、コンポーネントからストアにアクションをディスパッチするにはどうすればよいでしょうか?Reactの外ではstore.dispatch(action)を呼び出せますが、コンポーネントファイルではストアにアクセスできないため、コンポーネント内でdispatch関数自体にアクセスする方法が必要です。

React-ReduxのuseDispatchフックは、ストアのdispatchメソッドを結果として返します(実際、フックの実装はreturn store.dispatchです)。

したがって、アクションをディスパッチする必要があるコンポーネントではconst dispatch = useDispatch()と呼び出し、必要に応じてdispatch(someAction)を呼び出すことができます。

これを <Header> コンポーネントで試してみましょう。ユーザーに新しいTodo項目のテキストを入力させ、そのテキストを含む {type: 'todos/todoAdded'} アクションをディスパッチする必要があります。

"制御された入力"を使用する典型的なReactフォームコンポーネントを作成します。ユーザーが特にEnterキーを押したときにアクションをディスパッチします。

src/features/header/Header.js
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'

const Header = () => {
const [text, setText] = useState('')
const dispatch = useDispatch()

const handleChange = e => setText(e.target.value)

const handleKeyDown = e => {
const trimmedText = e.target.value.trim()
// If the user pressed the Enter key:
if (e.key === 'Enter' && trimmedText) {
// Dispatch the "todo added" action with this text
dispatch({ type: 'todos/todoAdded', payload: trimmedText })
// And clear out the text input
setText('')
}
}

return (
<input
type="text"
placeholder="What needs to be done?"
autoFocus={true}
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
)
}

export default Header

Provider でストアを渡す

コンポーネントはストアから状態を読み取り、ストアにアクションをディスパッチできるようになりました。しかし、まだ重要な点が欠けています。React-Reduxフックはどこでどのように正しいReduxストアを見つけているのでしょうか?フックはJavaScript関数であり、自動的に store.js からストアをインポートすることはできません。

代わりに、コンポーネントで使用するストアをReact-Reduxに明示的に指定する必要があります。<App> 全体を <Provider> コンポーネントで囲み、Reduxストアをpropとして <Provider> に渡すことで実現します。これを行うと、アプリケーション内のすべてのコンポーネントが必要に応じてReduxストアにアクセスできるようになります。

これをメインの index.js ファイルに追加しましょう:

src/index.js
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'

import App from './App'
import store from './store'

const root = createRoot(document.getElementById('root'))

root.render(
// Render a `<Provider>` around the entire `<App>`,
// and pass the Redux store to it as a prop
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)

これでReactとReact-Reduxを使用するための主要な部分を網羅しました:

  • Reactコンポーネントでデータを読み取るには useSelector フックを呼び出す

  • Reactコンポーネントでアクションをディスパッチするには useDispatch フックを呼び出す

  • 他のコンポーネントがストアと通信できるように <Provider store={store}><App> コンポーネント全体を囲む

これで実際にアプリと対話できるはずです!これまでの動作するUIは以下の通りです:

次に、Todoアプリでこれらを組み合わせて使用するさらにいくつかの方法を見ていきましょう。

React-Reduxのパターン

グローバルステート、コンポーネントステート、フォーム

「アプリの状態はすべてReduxストアに置かなければならないのか?」と疑問に思うかもしれません。

答えはNOです。アプリ全体で必要なグローバル状態はReduxストアに置き、一箇所だけで必要な状態はコンポーネント内に保持するべきです

これの良い例が先に書いた <Header> コンポーネントです。入力の onChange ハンドラでアクションをディスパッチし、リデューサーで保持することで、現在のテキスト入力文字列をReduxストアに保持することもできました。しかし、それにメリットはありません。そのテキスト文字列が使用されるのはこの <Header> コンポーネントだけだからです。

したがって、その値は <Header> コンポーネント内の useState フックで保持することが理にかなっています。

同様に、isDropdownOpenというブール値フラグがある場合、アプリ内の他のコンポーネントはこれに関心を持たないため、このコンポーネント内でローカルに保持すべきです。

ヒント

React + Reduxアプリでは、グローバル状態はReduxストアに、ローカル状態はReactコンポーネントに保持します。

どこに配置すべきか迷った場合、以下の一般的な経験則が参考になります:

  • アプリケーションの他の部分がこのデータを気にしますか?
  • この元データからさらに派生データを作成する必要がありますか?
  • 同じデータが複数のコンポーネントで使用されていますか?
  • 特定の時点に状態を復元できることに価値がありますか?(タイムトラベルデバッグなど)
  • データをキャッシュしたいですか?(再リクエストする代わりに、すでに存在する状態を使用する)
  • UIコンポーネントのホットリロード中にこのデータの一貫性を保ちたいですか?(内部状態が失われる可能性がある場合)

これは一般的にReduxでのフォームの扱い方を考える良い例でもあります。ほとんどのフォームの状態はReduxに保持すべきではありません。代わりに、編集中のデータはフォームコンポーネント内で保持し、ユーザーの操作が完了した時点でReduxアクションをディスパッチしてストアを更新します。

コンポーネント内での複数セレクターの使用

現時点では<TodoList>コンポーネントのみがストアからデータを読み取っています。<Footer>コンポーネントもデータを読み取る場合の実装例を見てみましょう。

<Footer>が知る必要がある情報は3つあります:

  • 完了済みtodoの数

  • 現在の「ステータス」フィルター値

  • 選択中の「色」カテゴリフィルターリスト

これらの値をコンポーネントにどう読み込むのでしょうか?

1つのコンポーネント内でuseSelectorを複数回呼び出せます。これは実際に推奨される方法です。useSelectorの各呼び出しは可能な限り最小の状態を返すべきです

完了済みtodoを数えるセレクターは既に作成済みです。フィルター値については、ステータスフィルター値と色フィルター値の両方がstate.filtersスライスに存在します。コンポーネントが両方必要とするため、state.filtersオブジェクト全体を選択できます。

前述のように、入力処理をすべて<Footer>に実装するか、<StatusFilter>のような独立コンポーネントに分割できます。説明を簡潔にするため、入力処理の詳細実装は割愛し、データと変更ハンドラーコールバックをpropsとして受け取る小規模コンポーネントがあると仮定します。

この前提に基づくと、コンポーネントのReact-Redux関連部分は次のようになります:

src/features/footer/Footer.js
import React from 'react'
import { useSelector } from 'react-redux'

import { availableColors, capitalize } from '../filters/colors'
import { StatusFilters } from '../filters/filtersSlice'

// Omit other footer components

const Footer = () => {
const todosRemaining = useSelector(state => {
const uncompletedTodos = state.todos.filter(todo => !todo.completed)
return uncompletedTodos.length
})

const { status, colors } = useSelector(state => state.filters)

// omit placeholder change handlers

return (
<footer className="footer">
<div className="actions">
<h5>Actions</h5>
<button className="button">Mark All Completed</button>
<button className="button">Clear Completed</button>
</div>

<RemainingTodos count={todosRemaining} />
<StatusFilter value={status} onChange={onStatusChange} />
<ColorFilters value={colors} onChange={onColorChange} />
</footer>
)
}

export default Footer

IDによるリスト項目のデータ選択

現在、<TodoList>state.todos配列全体を読み取り、実際のtodoオブジェクトを各<TodoListItem>コンポーネントにpropsとして渡しています。

これは機能しますが、潜在的なパフォーマンス問題があります。

  • todoオブジェクトを変更すると、todo自体とstate.todos配列のコピーが作成され、各コピーはメモリ上で新しい参照となります

  • useSelectorが結果として新しい参照を検出すると、コンポーネントの再レンダリングが強制されます

  • したがって、_1つの_todoオブジェクトが更新されるたび(例:完了ステータスのトグル)、親コンポーネントである<TodoList>全体が再レンダリングされます

  • さらに、Reactはデフォルトで全ての子コンポーネントを再帰的に再レンダリングするため、実際には変更されていない_すべての_<TodoListItem>コンポーネントも再レンダリングされます!

コンポーネントの再レンダリング自体は問題ありません(ReactがDOM更新を判断する方法です)。しかし、実際に変更がない場合に多数のコンポーネントを再レンダリングすると、リストが大きすぎるときにパフォーマンス低下を招く可能性があります。

この問題に対処する方法はいくつかあります。1つは<TodoListItem>コンポーネントをReact.memo()でラップする方法で、propsが実際に変更されたときのみ再レンダリングされます。パフォーマンス改善策として有効ですが、子コンポーネントは実際の変更発生まで同一のpropsを受け取る必要があります。各<TodoListItem>はtodo項目をpropsとして受け取るため、実際に変更されるのは1項目のみです。

別の方法は、<TodoList>コンポーネントがストアからtodo IDの配列のみを読み取り、これらのIDを子コンポーネント<TodoListItem>にpropsとして渡す方式です。各<TodoListItem>はIDを使用して必要なtodoオブジェクトを取得できます。

このアプローチを実際に試してみましょう。

src/features/todos/TodoList.js
import React from 'react'
import { useSelector } from 'react-redux'
import TodoListItem from './TodoListItem'

const selectTodoIds = state => state.todos.map(todo => todo.id)

const TodoList = () => {
const todoIds = useSelector(selectTodoIds)

const renderedListItems = todoIds.map(todoId => {
return <TodoListItem key={todoId} id={todoId} />
})

return <ul className="todo-list">{renderedListItems}</ul>
}

今回は <TodoList> コンポーネントでストアからtodoのID配列のみを取得し、各 todoIdid プロパティとして子コンポーネントの <TodoListItem> に渡します。

次に <TodoListItem> 内で、このID値を使ってtodoアイテムを読み取ります。また <TodoListItem> を更新して、todoのIDに基づいて「toggled」アクションをディスパッチできるようにします。

src/features/todos/TodoListItem.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'

import { availableColors, capitalize } from '../filters/colors'

const selectTodoById = (state, todoId) => {
return state.todos.find(todo => todo.id === todoId)
}

// Destructure `props.id`, since we only need the ID value
const TodoListItem = ({ id }) => {
// Call our `selectTodoById` with the state _and_ the ID value
const todo = useSelector(state => selectTodoById(state, id))
const { text, completed, color } = todo

const dispatch = useDispatch()

const handleCompletedChanged = () => {
dispatch({ type: 'todos/todoToggled', payload: todo.id })
}

// omit other change handlers
// omit other list item rendering logic and contents

return (
<li>
<div className="view">{/* omit other rendering output */}</div>
</li>
)
}

export default TodoListItem

ただし問題があります。セレクターで新しい配列参照を返すとコンポーネントが毎回再レンダリングされると前述しましたが、現在 <TodoList> では新しいID配列を返しています。todoをトグルする場合、ID配列の内容は同じであるべきです。表示するtodoアイテムは変わっておらず、追加や削除もされていないからです。しかしIDを含む配列自体は新しい参照となるため、<TodoList> は本来不要なタイミングで再レンダリングされてしまいます。

この問題の解決策の一つは、useSelector が値の変更を確認する比較方法を変更することです。useSelector は第2引数として比較関数を受け取れます。比較関数は新旧の値を受け取り、同じとみなされる場合は true を返します。値が同じ場合、useSelector はコンポーネントの再レンダリングを発生させません。

React-Reduxには shallowEqual 比較関数があり、配列内のアイテムが同じかどうかをチェックできます。これを試してみましょう:

src/features/todos/TodoList.js
import React from 'react'
import { useSelector, shallowEqual } from 'react-redux'
import TodoListItem from './TodoListItem'

const selectTodoIds = state => state.todos.map(todo => todo.id)

const TodoList = () => {
const todoIds = useSelector(selectTodoIds, shallowEqual)

const renderedListItems = todoIds.map(todoId => {
return <TodoListItem key={todoId} id={todoId} />
})

return <ul className="todo-list">{renderedListItems}</ul>
}

これでtodoアイテムをトグルしてもIDリストは同じとみなされ、<TodoList> は再レンダリングされなくなります。該当する1つの <TodoListItem> は更新されたtodoオブジェクトを受け取って再レンダリングしますが、他のアイテムは既存のtodoオブジェクトを保持するため再レンダリングされません。

前述のように、コンポーネントのレンダリングを最適化するには「メモ化されたセレクター」と呼ばれる特殊なセレクター関数も利用でき、別のセクションでその使用方法を説明します。

学んだこと

これで動作するtodoアプリが完成しました!アプリはストアを作成し、<Provider> を使ってReact UI層にストアを渡し、Reactコンポーネント内で useSelectoruseDispatch を呼び出してストアと通信します。

情報

残りのUI機能を自身で実装してみましょう!追加が必要な機能は以下の通りです:

  • <TodoListItem> コンポーネントで useDispatch フックを使用し、カテゴリ色の変更やtodo削除のアクションをディスパッチ
  • <Footer>useDispatch フックを使用し、全todoの完了マーク付け、完了済みtodoのクリア、フィルター値の変更アクションをディスパッチ

フィルターの実装については第7章: 標準的なReduxパターンで説明します。

簡潔さを優先して省略したコンポーネントやセクションを含め、アプリの現在の状態を見てみましょう:

まとめ
  • ReduxストアはあらゆるUI層で使用可能
    • UIコードは常にストアを購読し、最新状態を取得して自身を再描画する
  • React-ReduxはReact向け公式Reduxバインディングライブラリ
    • react-redux パッケージとして別途インストールする
  • useSelector フックでReactコンポーネントがストアからデータを取得
    • セレクター関数はストア全体の state を引数に取り、その状態に基づいた値を返す
    • useSelector はセレクター関数を呼び出し、その結果を返す
    • useSelector はストアを購読し、アクションがディスパッチされるたびにセレクターを再実行する
    • セレクター結果が変化すると、useSelector は新しいデータでコンポーネントを再レンダリングさせる
  • useDispatch フックでReactコンポーネントがストアにアクションをディスパッチ
    • useDispatch は実際の store.dispatch 関数を返す
    • コンポーネント内で必要に応じて dispatch(action) を呼び出せる
  • <Provider> コンポーネントが他のReactコンポーネントにストアを提供
    • アプリ全体(<App>)を <Provider store={store}> でラップする

次のステップ

UIが動作するようになったので、次はReduxアプリをサーバーと通信させる方法を見ていきましょう。パート6: 非同期ロジックでは、タイムアウトやHTTPリクエストのような非同期ロジックをReduxのデータフローにどのように組み込むかについて説明します。