2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Redux Basics (5): Usage with React 翻訳

Last updated at Posted at 2016-10-31

はじめに

ReduxのBasicsの翻訳の続きです。今回はUsage with Reactの翻訳となります。翻訳で間違いがあると思いますがそこはご了承ください。

翻訳は下記ページの10/1時点のものとなります。
http://redux.js.org/docs/basics/UsageWithReact.html

翻訳ここから

Reactとの使い方

開始時から、我々はReactとReduxが関係を持たないことを強調する必要がありました。あなたはReact, Angular, Ember, jQuery, vanilla JavaScriptとともにReduxアプリを書くことができます。

これは、ReduxはReactDekuのようなライブラリとともに特によく動き、なぜなら状態の関数としてのUIを描画し、ReduxはActionへのレスポンスの中で状態の更新を発行するからです。

我々はシンプルなTodoアプリを構築するためにReactを使います。

React Reduxのインストール

React bindingsは標準でReduxを含みません。あなたは明示的にそれらをインストールする必要があります。

npm install --save react-redux

もしnpmを使わないならば、unpkgから最新のUMDビルド(developmentproductionビルド)を取ってくると良いでしょう。もしあなたが<script>タグを経由してページにそれを追加するならば、UMDビルドはwindow.ReactReduxと呼ばれるグローバルを伝えます。

PresentationalコンポーネントとContainerコンポーネント

ReduxのためのReact bindingsは分割したPresentationalとContainerコンポーネントのアイデアを包含します。もしこれらの条件に精通していない場合、最初にこれについて読み、それから戻ってきてください。

記事を読み終わりましたか?これらの違いを詳しく述べましょう:

Presentationalコンポーネント Containerコンポーネント
目的 どのように物が見えるか(マークアップ、スタイル) どのようにものが動くか(データの取得、状態の更新)
Reduxへの気づき No Yes
データの読み込み propsからデータを読み込む Redux stateへ購読する
データの変更 proprsからコールバックを実行する Redux Actionを実行する
書き込み 手によって たいていReact Reduxによって生成される

我々が書くコンポーネントの多くは、Presentationalでしょうが、我々は2,3のRedux Storeへそれらを接続するContainerコンポーネントを必要とします。

専門的に言うと、store.subscribe()を使うことで手でContainerコンポーネントを書くこともできます。我々はこれをするよう忠告しません、なぜなら、React Reduxは手でするのは難しい多くのパフォーマンス最適化をするからです。この理由により、Containerコンポーネントを書くよりもむしろ、我々は以下で見るような、React Reduxによって提供されるconnect()関数を使ってそれらを生成します。

コンポーネント階層の設計

どのように我々がルートの状態オブジェクトの形を設計したかを覚えていますか?我々が設計したUI階層をそれに調和させる時がきました。これはReduxの特別なタスクではありません。Reactを考えることは過程を説明する偉大なチュートリアルです。

我々の設計指示はシンプルです。我々はTodoアイテムのリストを表示したいです。クリックで、Todoアイテムは完了したとして線を引いて消されます。我々はユーザーが新しいTodoを追加したフィールドを表示したいです。フッターで、我々はすべてを、完了だけを、またはアクティブなTodoのみを表示するためにトグルを表示したいです。

Presentationalコンポーネント

下記のPresentationalコンポーネントとこの要約で明らかになるpropsを見ましょう。

  • **TodoList**は見えるTodoを表示しているリストです。
    • todos: Array{ id, text, completed }の形のTodoアイテムの配列です。
    • onTodoClick(id: number)はTodoがクリックされたときに呼び出されるコールバックです。
  • **Todo**は一つのTodoアイテムです。
    • text : stringは表示するテキストです。
    • completed: booleanはTodoが線を引いて消すかどうかです。
    • onClick()はTodoがクリックされたときに呼び出されるコールバックです。
  • **Link**はコールバックのリンクです。
    • onClick()はリンクがクリックされたときに呼び出されるコールバックです。
  • **Footer**はユーザーが現在見えるTodoを変えたことを示す場所です。
  • **App**は全てを描画するルートコンポーネントです。

それらは_見た目_を説明しますが、データが_どこから_来るかは知らなく、_どのように_変わるか知りません。それらはただそれらに与えられたものを描画するだけです。もしあなたがReduxから何か他のものへ移住するなら、これらのコンポーネントを正確に同じに保つことができるでしょう。それらはReduxへの依存を持ちません。

Containerコンポーネント

我々はまたReduxへPresentationalコンポーネントを接続するためのいくつかのContainerコンポーネントを必要とします。例えば、TodoListのPresentationalコンポーネントはReduxのStoreへ購読し、どのように現在のvisibilityフィルタが適用されるかを示すvisibleTodoListのようなコンテナを必要とします。visibilityフィルタを変更することは、我々はクリックに適切なActionを呼び出すLinkを描画するFilterLinkのContainerコンポーネントを提供します。

  • **VisibleTodoList**は現在のvisibilityフィルタによりTodoを除去し、TodoListを描画します。
  • **FilterLink**は現在のvisibilityフィルタを取得し、Linkを描画します。
    • filter: stringはvisibilityフィルタを表現するものです。

他のコンポーネント

時々、あるコンポーネントがPresentationalコンポーネントかContainerコンポーネントかどうかを話すのは難しいです。例えば、時々フォームと関数はこの小さい場合のように本当に連結しています:

  • **AddTodo**は“追加”ボタンの入力フィールドです。

専門的に言うと、我々は二つのコンポーネントにそれを分割できますが、このステージでは早すぎます。Presentationとロジックを大変小さなコンポーネントに混ぜ合わせることがいいでしょう。成長したら、どのように分割するかもっと明らかにし、そして混ぜ合わせを解消しましょう。

コンポーネントの実装

コンポーネントを書いてみましょう!我々はPresentationalコンポーネントを始め、そして我々はまだReduxへバインドすることについては考える必要はありません。

Presentationalコンポーネント

これらは全て通常のReactコンポーネントで、我々は詳細は調べません。我々はローカルの状態やライフサイクルメソッドを使うことのない、関数で状態なしのコンポーネントを書きます。これはPresentationalコンポーネントが関数でなくてはならないことを意味しません。この方法でこれらを定義するのにただ簡単です。もし、そしてあなたがローカルの状態やライフサイクルメソッド、パフォーマンス最適化を追加することが必要なとき、あなたはクラスにそれらを変換できます。

components/Todo.js

import React, { PropTypes } from 'react'

const Todo = ({ onClick, completed, text }) => (
  <li
    onClick={onClick}
    style={{
      textDecoration: completed ? 'line-through' : 'none'
    }}
  >
    {text}
  </li>
)

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired
}

export default Todo

components/TodoList.js

import React, { PropTypes } from 'react'
import Todo from './Todo'

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

TodoList.propTypes = {
  todos: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }).isRequired).isRequired,
  onTodoClick: PropTypes.func.isRequired
}

export default TodoList

components/Link.js

import React, { PropTypes } from 'react'

const Link = ({ active, children, onClick }) => {
  if (active) {
    return <span>{children}</span>
  }

  return (
    <a href="#"
       onClick={e => {
         e.preventDefault()
         onClick()
       }}
    >
      {children}
    </a>
  )
}

Link.propTypes = {
  active: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func.isRequired
}

export default Link

components/Footer.js

import React from 'react'
import FilterLink from '../containers/FilterLink'

const Footer = () => (
  <p>
    Show:
    {" "}
    <FilterLink filter="SHOW_ALL">
      All
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_ACTIVE">
      Active
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_COMPLETED">
      Completed
    </FilterLink>
  </p>
)

export default Footer

components/App.js

import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

export default App

Containerコンポーネント

今、あるContainerを作ることによってこれらPresentationalコンポーネントをReduxへ引っ掛ける時です。専門的に言うと、Containerコンポーネントは、Reduxの状態ツリーの一部を読むためにstore.subscribe()を使い、描画するためにPresentationalコンポーネントへpropsを提供する、ただのReactコンポーネントです。あなたは手でContainerコンポーネントを書くことができますが、我々は代わりにReact Reduxライブラリのconnect()関数でContainerコンポーネントを生成することを提案します。connect()関数は不必要な再描画を防ぐための多くの便利な最適化を提供します(この一つの結果は、あなたはshouldComponentUpdateを実行することのReactパフォーマンスの提案について心配することはありません。)。

connect()を使うことで、あなたは、あなたがラッピングするPresentationalコンポーネントへ渡したいpropsへ現在のReduxのStoreの状態をどのように転換するかを説明するmapStateToPropsと呼ばれる特別な関数を定義する必要があります。例えば、VisibleTodoListTodoListに渡すためにtodosを計算することを必要とし、そして我々はstate.visibilityFilterによりstate.todosを除く関数を定義し、mapStateToPropsのなかでそれを使います:

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

状態を読むことに加えて、ContainerコンポーネントはActionを呼び出します。同様に、あなたはdispatch()メソッドを受け取り、Presentationalコンポーネントへ差し込みたいpropsのコールバックを返すmapDispatchToProps()と呼ばれる関数を定義します。例えば、我々はTodoListコンポーネントへonTodoClickと呼ばれるpropを差し込むためにVisibilityTodoListが欲しく、TOGGLE_TODOActionを呼び出すためのonTodoClickが欲しいです。

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

最後に、我々はconnect()を呼ぶことによってVisibleTodoListを作り、これら二つの関数を渡します:

import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

これらはReact Redux APIの基礎ですが、2,3のショートカットと強力なオプションがあり、我々はあなたに詳細のドキュメントを確認することを推奨します。あなたがあまりに頻繁に新しいオブジェクトを作るmapStateToPropsについて心配な場合は、あなたはreselect派生データの作成について学ぶとよいでしょう。

下記で定義されたContainerコンポーネントの残りを見つけてください。

containers/FilterLink.js

import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: () => {
      dispatch(setVisibilityFilter(ownProps.filter))
    }
  }
}

const FilterLink = connect(
  mapStateToProps,
  mapDispatchToProps
)(Link)

export default FilterLink

containers/VisibleTodoList.js

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

他のコンポーネント

containers/AddTodo.j

import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

let AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form onSubmit={e => {
        e.preventDefault()
        if (!input.value.trim()) {
          return
        }
        dispatch(addTodo(input.value))
        input.value = ''
      }}>
        <input ref={node => {
          input = node
        }} />
        <button type="submit">
          Add Todo
        </button>
      </form>
    </div>
  )
}
AddTodo = connect()(AddTodo)

export default AddTodo

Storeへ渡す

全てのContainerコンポーネントはReduxのStoreへのアクセスを必要とするので、それらは購読できます。一つのオプションは全てのContainerコンポーネントへpropとしてそれを渡すことになります。しかしながらそれはstoreをPresentationalコンポーネントでさえ繋がなければならないとして、うんざりします。なぜならコンポーネントツリー野中でContainerが深く描画されることが起こるからです。

我々が推奨するオプションは、魔法のようにStoreを明示的に渡すことなしにアプリケーションの全てのContainerコンポーネントへ可能にする<Provider>と呼ばれる特別なReact Reduxコンポーネントを使うことです。あなたはルートコンポーネントを描画するときにただ一回それを使う必要があります。

index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

次のステップ

あなたが得た知識をもっと身につけるためにこのチュートリアルの完全なソースコードを読みましょう。それからどのようにネットワークのリクエストとルーティングを処理するかを学ぶために上級チュートリアルへ直行しましょう。

翻訳ここまで

終わりに

次のステップのExample: Todo Listはコードだけのステップとなりますので翻訳はこちらで最後となります。

今までのコードの構成と次のステップであるExample: Todo Listのコードが異なっているのと、コードにコメントがあったほうがわかりやすいので、コメント付き全コードを上げられたら上げようと思います。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?