LoginSignup
3
4

More than 3 years have passed since last update.

【定期更新】Redux公式チュートリアルのざっくりした和訳と流れ

Last updated at Posted at 2020-06-17

気が向いたときに更新していきます。
Redux Toolkit公式チュートリアル、Redux公式チュートリアルをかいつまんでざっくりと翻訳したものです。
「」内は、進めるにあたっての自分の思考の流れを思い出すためにメモしているものです。
誤りが多いかもしれませんが大目にみてください。

This tutorial assumes that you are already familiar with the concepts of the core Redux library, as well as how to use it with React. If you aren't, please take some time to read through the Redux docs and React-Redux docs first, as the explanations here focus on how RTK usage differs from "typical" Redux code.

「Reduxを先にやれってことか。」

Redux

Installation

Create a React Redux App
The recommended way to start new apps with React and Redux is by using the official Redux+JS template for Create React App, which takes advantage of Redux Toolkit and React Redux's integration with React components.

「オススメらしいからこのやり方でやろ。」

$ mkdir ReduxTutorial
$ cd ReduxTutorial
$ npx create-react-app my-app --template redux

Actioins

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store. You send them to the store using store.dispatch().

Actionは、アプリケーションからstoreにデータを送信する情報のペイロードです。 それらはstoreの唯一の情報源です。 それらは、store.dispatch()を使用してstoreに送信します。

(補足)
ペイロード
①乗客・貨物などの有料荷重。
②航空機やミサイルの有効搭載量、搭載物。
③ロケットの打ち上げの性能を示す指標の一。所定の高度に打ち上げることのできる人工衛星の重量。
④人件費。

Actionは、storeに送られる唯一無二の存在なんやな〜。 store.dispatch()はよくわからんからほっとこ。」

Actions are plain JavaScript objects. Actions must have a type property that indicates the type of action being performed. Types should typically be defined as string constants.

Actionは単純なJavaScriptオブジェクトです。 Actionには、実行されるアクションのタイプを示すtypeプロパティが必要です。 typeは通常、文字列定数として定義する必要があります。

const ADD_TODO = 'ADD_TODO'
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

Actionの実態は、このオブジェクトひとまとまりのことみたい。typeは定数で定義するみたいやなー。オブジェクトの中のtextはよくわからんけどほっとこ。」

Action Creators

Action creators are exactly that—functions that create actions. It's easy to conflate the terms “action” and “action creator”, so do your best to use the proper term.
In Redux, action creators simply return an action:

Action creatorsは、まさにそれ、つまりActionを作成する関数です。 「アクション」と「アクションクリエーター」という用語を混同するのは簡単なので、適切な用語を使用するように最善を尽くしてください。Reduxでは、Action creatorsは単にActionを返します。

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Action creatorsは、addTodo関数自体をさしてて、Action、つまり、オブジェクトひとまとまりを返すんやなー。」

「さっきのよく分からんかったtextが引数で渡ってきてるから、多分、追加したいTODOをAction creators、つまり、addTodo関数に渡すんやろうなー。」

text は、 text: textの省略ってことも覚えとかな混乱しそうやな。。。」

Source Code

action.js
/*
 * action types
 */

export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'

/*
 * other constants
 */

export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE'
}

/*
 * action creators
 */

export function addTodo(text) {
  return { type: ADD_TODO, text }
}

export function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

export function setVisibilityFilter(filter) {
  return { type: SET_VISIBILITY_FILTER, filter }
}

other constants はあとで使うっぽいな」

Reducers

Reducersは、storeに送信されたActionに応じてアプリケーションの状態がどのように変化するかを指定します。 Actionは何が起こったかを説明するだけで、アプリケーションの状態がどのように変化するかを説明しないことに注意してください。

Actionはただのオブジェクトの塊としてstoreに送信されるんやな。だから、Reducersによる指示が必要になるんか。」

Designing the State Shape

{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}

「これからstateが出てきたら、実態はこんな形のオブジェクトってことをイメージしたらわかりやすいかもな。」

We'll start by specifying the initial state. Redux will call our reducer with an undefined state for the first time. This is our chance to return the initial state of our app:

最初に、初期状態を指定します。 Reduxは初めて、未定義の状態でReducersを呼び出します。 これは、アプリの初期状態を返す機会です。

import { VisibilityFilters } from './actions'

const initialState = {
  visibilityFilter: VisibilityFilters.SHOW_ALL,
  todos: []
}

function todoApp(state, action) {
  if (typeof state === 'undefined') {
    return initialState
  }

  // For now, don't handle any actions
  // and just return the state given to us.
  return state
}

「さっきよく分からんかったactions.jsVisibilityFiltersが使われてるな。 初期状態は『全部見せる』に設定してある。」
ReducerstodoApp関数のことみたいやな。状態(state)とActionを引数で受け取ってるな。状態(state)がなかったら初期状態を返すみたいや。」

Handling Actions

状態オブジェクトの外観を決定したので、そのためのReducersを作成する準備ができました。 Reducersは、前の状態とActionを取り、次の状態を返す純粋な関数です。

「状態(state)があったら、新しい状態(state)を返すってことやな。」

import {
  SET_VISIBILITY_FILTER,
  VisibilityFilters
} from './actions'

...

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

「importしてるSET_VISIBILITY_FILTERは、確かActiontypeのうちの一つやったな。」

「受け取ったActiontypeSET_VISIBILITY_FILTERだったら。。。っていう処理の流れなんか。 Actionを受け取るイメージがまだあんまりわかないけどほっとこ。」

Object.assign()MDNによると、オブジェクトを合体させるらしい。 initialStateみたいな{visibilityFilter: 〇〇, todos: [〇〇]} の形になるってことやな。」

action.filterfilterは、Action creatorssetVisibilityFilter 関数の引数やったな。。。 実態はまだ分からんな。」

Handling More Actions

import {
  ADD_TODO,
  TOGGLE_TODO,
  SET_VISIBILITY_FILTER,
  VisibilityFilters
} from './actions'

...

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
    default:
      return state
  }
}

「渡ってきたActiontypeADD_TODOやったら、状態(state)に、textcompletedの情報が足された状態(state)を返すんやなー。」

case TOGGLE_TODO:
  return Object.assign({}, state, {
    todos: state.todos.map((todo, index) => {
      if (index === action.index) {
        return Object.assign({}, todo, {
          completed: !todo.completed
        })
      }
      return todo
    })
  })

「渡ってきたActiontypeTOGGLE_TODOやったら、状態(state)のtodoを一つづつ調べて、渡ってきたActionindexと一致したtodocompletedの状態を変えた状態(state)を返すんやなー。」

Splitting Reducers

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
    case TOGGLE_TODO:
      return Object.assign({}, state, {
        todos: state.todos.map((todo, index) => {
          if (index === action.index) {
            return Object.assign({}, todo, {
              completed: !todo.completed
            })
          }
          return todo
        })
      })
    default:
      return state
  }
}

「今までのReducerはこんな感じやな。」

理解しやすくする方法はありますか? todosvisibilityFilterは完全に独立して更新されるようです。 時々、状態フィールドは互いに依存し、より多くの考慮が必要ですが、私たちの場合、更新タスクを別の関数に簡単に分割できます:

「確かに。case SET_VISIBILITY_FILTERの時だけtodosを更新せず、visibilityFilterの値を変えるっていう、違う仕事をしてる。」

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: todos(state.todos, action)
      })
    case TOGGLE_TODO:
      return Object.assign({}, state, {
        todos: todos(state.todos, action)
      })
    default:
      return state
  }
}

todostateも受け入れますが、状態は配列です。 これで、todoApptodosに管理する状態のスライスのみを提供し、todosはそのスライスだけを更新する方法を知っています。 これはreducer compositionと呼ばれ、Reduxアプリを構築する基本的なパターンです

todoApp関数は、todo関数に、Actionに応じてtodoを変更してやーって感じで投げてるのか。」
todo関数は、visibilityFilterに対しては我関せずって感じになったな。」

Reducerの構成についてさらに詳しく見てみましょう。 また、visibilityFilterのみを管理するReducerを抽出できますか? 私たちはできる。
インポートの下で、ES6 Object Destructuringを使用してSHOW_ALLを宣言しましょう。

const { SHOW_ALL } = VisibilityFilters
function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

visibilityFilter関数は、todoに対しては我関せずって感じになったな。」

これで、メインのReducerを、stateの一部を管理するReducerを呼び出し、それらを単一のオブジェクトに結合する関数として書き換えることができます。 また、完全な初期状態を知る必要もありません。 子Reducerは、最初は未定義の場合、初期状態を返すだけで十分です。

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

「スッキリしたな。todoApp関数は、ちゃんと前みたDesigning the State Shapeのようなstateを返しているな。」

これらのReducerのそれぞれがグローバルstateの独自の部分を管理していることに注意してください。 状態パラメーターはReducerごとに異なり、管理する状態の一部に対応します。
これはすでに見栄えが良いです! アプリが大きい場合、Reducerを個別のファイルに分割し、それらを完全に独立させて、異なるデータドメインを管理できます。

最後に、Reduxは、上記のtodoAppが現在行っているのと同じボイラープレートロジックを実行するCombineReducers()と呼ばれるユーティリティを提供します。 その助けを借りて、todoAppを次のように書き換えることができます。

import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

このコードは下と同等です。

export default function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

combineReducersの実態はこれだったのか!!(感動)」

Source Code

reducers.js
import { combineReducers } from 'redux'
import {
  ADD_TODO,
  TOGGLE_TODO,
  SET_VISIBILITY_FILTER,
  VisibilityFilters
} from './actions'
const { SHOW_ALL } = VisibilityFilters

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

Store

前のセクションでは、「起こったこと」に関する事実を表すActionと、それらのActionに従ってstateを更新するReducerを定義しました。

Storeは、それらをまとめるオブジェクトです。 Storeには以下の責任があります。
・アプリケーションのstateを保持します。
getState()を介してstateにアクセスできます。
dispatch(action)を介してstateを更新できるようにします。
subscribe(listener)を介してリスナーを登録します。
subscribe(listener)によって返される関数を介してリスナーの登録解除を処理します。
Reduxアプリケーションにはストアが1つしかないことに注意してください。 データ処理ロジックを分割する場合は、多くのストアの代わりにリデューサー構成を使用します。

Reducerがあれば、簡単にStoreを作成できます。 前のセクションでは、combineReducers()を使用して複数のReducerを1つに結合しました。 これをインポートして、createStore()に渡します。

import { createStore } from 'redux'
import todoApp from './reducers'
const store = createStore(todoApp)

Dispatching Actions

import {
  addTodo,
  toggleTodo,
  setVisibilityFilter,
  VisibilityFilters
} from './actions'

// Log the initial state
console.log(store.getState())

// Every time the state changes, log it
// Note that subscribe() returns a function for unregistering the listener
const unsubscribe = store.subscribe(() => console.log(store.getState()))

// Dispatch some actions
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// Stop listening to state updates
unsubscribe()

これにより、Storeが保持するstateがどのように変化するかを確認できます。

スクリーンショット 2020-07-12 18.14.13.png

Actionは、アプリケーションからstoreにデータを送信する情報のペイロードです。 それらはstoreの唯一の情報源です。 それらは、store.dispatch()を使用してstoreに送信します。

ってこのことやったんやなー。
引数のaddTodo関数、すなわち、action creatorによってActionが渡されてるんや。」
Actionが渡されたstoreの中でReducerが動いてるイメージやな。」

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4