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

Redux入門

Reduxは、予測可能で保守しやすいグローバルな状態管理のためのJSライブラリです。

一貫した動作をし、さまざまな環境(クライアント、サーバー、ネイティブ)で実行でき、テストが容易なアプリケーションを作成するのに役立ちます。さらに、タイムトラベルデバッガと組み合わせたライブコード編集など、優れた開発者エクスペリエンスを提供します。

Reduxは、Reactや他のビューライブラリと一緒に使用できます。非常に軽量(依存関係を含めて2kB)ですが、利用可能なアドオンの大規模なエコシステムがあります。

Redux Toolkitは、Reduxロジックを記述するための公式推奨アプローチです。Reduxコアをラップし、Reduxアプリを構築する上で不可欠だと考えるパッケージと機能が含まれています。Redux Toolkitは、推奨されるベストプラクティスを組み込み、ほとんどのReduxタスクを簡素化し、よくある間違いを防ぎ、Reduxアプリケーションを簡単に記述できるようにします。

RTKには、ストアの設定リデューサーの作成と不変更新ロジックの記述、さらには状態の「スライス」全体を一度に作成するなど、多くの一般的なユースケースを簡素化するのに役立つユーティリティが含まれています。

初めてReduxを使用するユーザーが最初のプロジェクトを設定する場合でも、既存のアプリケーションを簡素化したい経験豊富なユーザーでも、Redux Toolkitを使用すると、Reduxコードをより良くすることができます。

インストール

Redux Toolkit

Redux Toolkitは、モジュールバンドラーまたはNodeアプリケーションで使用するためのNPMパッケージとして利用可能です

# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit

React Reduxアプリの作成

ReactとReduxで新しいアプリを開始する推奨される方法は、Vite用の公式Redux+TSテンプレートを使用するか、Nextのwith-reduxテンプレートを使用して新しいNext.jsプロジェクトを作成することです。

これらはどちらも、そのビルドツール用にRedux ToolkitとReact-Reduxが適切に構成されており、Redux Toolkitのいくつかの機能の使用方法を示す小さなサンプルアプリが付属しています。

# Vite with our Redux+TS template
# (using the `degit` tool to clone and extract the template)
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app

# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-app

現在、公式のReact Nativeテンプレートはありませんが、標準のReact NativeおよびExpoにはこれらのテンプレートをお勧めします

Redux Core

Reduxコアライブラリは、モジュールバンドラーまたはNodeアプリケーションで使用するためのNPMパッケージとして利用可能です

# NPM
npm install redux

# Yarn
yarn add redux

このパッケージには、ブラウザで直接<script type="module">タグとして使用できるプリコンパイル済みのESMビルドが含まれています。

詳細については、インストールページを参照してください。

基本例

アプリのグローバルな状態全体は、単一のストア内のオブジェクトツリーに保存されます。状態ツリーを変更する唯一の方法は、何が起こったかを記述するオブジェクトであるアクションを作成し、ストアにディスパッチすることです。アクションに応じて状態がどのように更新されるかを指定するには、古い状態とアクションに基づいて新しい状態を計算する純粋なリデューサー関数を記述します。

Redux Toolkitは、Reduxロジックの記述とストアの設定のプロセスを簡素化します。Redux Toolkitを使用すると、基本的なアプリロジックは次のようになります

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
incremented: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decremented: state => {
state.value -= 1
}
}
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
reducer: counterSlice.reducer
})

// Can still subscribe to the store
store.subscribe(() => console.log(store.getState()))

// Still pass action objects to `dispatch`, but they're created for us
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

状態を直接変更する代わりに、アクションと呼ばれるプレーンオブジェクトで発生させたい変更を指定します。次に、リデューサーと呼ばれる特別な関数を記述して、すべてのアクションがアプリケーション全体の状態をどのように変換するかを決定します。

一般的なReduxアプリでは、単一のルートリデューサー関数を持つ単一のストアがあります。アプリが成長するにつれて、ルートリデューサーを、状態ツリーのさまざまな部分で独立して動作する小さなリデューサーに分割します。これは、Reactアプリに1つのルートコンポーネントしかないが、多くの小さなコンポーネントで構成されているのとまったく同じです。

このアーキテクチャは、カウンターアプリには多く見えるかもしれませんが、このパターンの美しさは、大規模で複雑なアプリにどのようにスケールするかです。また、開発ツールを非常に強力にします。これは、すべての変更をその原因となったアクションに追跡できるためです。ユーザーセッションを記録し、すべてのアクションを再生するだけで再現できます。

Redux Toolkitを使用すると、同じReduxの動作とデータフローに従いながら、読みやすいより短いロジックを記述できます。

レガシー例

比較のために、元のReduxレガシー構文(抽象化なし)は次のようになります

import { createStore } from 'redux'

/**
* This is a reducer - a function that takes a current state value and an
* action object describing "what happened", and returns a new state value.
* A reducer's function signature is: (state, action) => newState
*
* The Redux state should contain only plain JS objects, arrays, and primitives.
* The root state value is usually an object. It's important that you should
* not mutate the state object, but return a new object if the state changes.
*
* You can use any conditional logic you want in a reducer. In this example,
* we use a switch statement, but it's not required.
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

Reduxを学ぶ

Reduxを学習するのに役立つさまざまなリソースを用意しています。

Redux Essentialsチュートリアル

Redux Essentialsチュートリアルは、「Reduxを正しく使用する方法」を最新の推奨APIとベストプラクティスを使用して教える「トップダウン」チュートリアルです。最初にここから始めることをお勧めします。

Redux Fundamentalsチュートリアル

Redux Fundamentalsチュートリアルは、「Reduxがどのように機能するか」を第一原理から抽象化なしで、標準的なReduxの使用パターンが存在する理由を教える「ボトムアップ」チュートリアルです。

Modern Reduxライブストリームを学ぶ

ReduxメンテナーのMark Eriksonが「Learn with Jason」ショーに出演し、今日のReduxの使用方法について説明しました。このショーには、Redux ToolkitとReact-ReduxフックをTypeScriptで使用する方法、および新しいRTK QueryデータフェッチAPIを示すライブコーディングされたサンプルアプリが含まれています。

トランスクリプトとサンプルアプリのソースへのリンクについては、「Learn Modern Redux」ショーのメモページを参照してください。

追加のチュートリアル

その他のリソース

ヘルプとディスカッション

Reactiflux Discordコミュニティの#reduxチャンネルは、Reduxの学習と使用に関するすべての質問に対する公式リソースです。Reactifluxは、くつろぎ、質問をし、学ぶのに最適な場所です。ぜひご参加ください!

Stack Overflow#reduxタグを使って質問することもできます。

バグレポートがある場合や、その他のフィードバックを残す必要がある場合は、Githubリポジトリにissueを提出してください

Reduxを使用すべきですか?

Reduxは状態を整理するための貴重なツールですが、それがあなたの状況に適しているかどうかを検討する必要があります。誰かにそうすべきだと言われたからという理由だけでReduxを使用しないでください。それを使用することの潜在的なメリットとトレードオフを理解するために時間をかけてください

Reduxを使用するのが理にかなっている場合のいくつかの提案を以下に示します。

  • 時間とともに変化する適切な量のデータがある場合
  • 状態の単一の真実の情報源が必要な場合
  • すべての状態をトップレベルのコンポーネントに保持することがもはや十分ではないと感じる場合

Reduxの意図された使用方法に関する詳細な考察については、以下を参照してください。