はじめに
- Redux Basics (1): Actions 翻訳
- Redux Basics (2): Reducers 翻訳
- Redux Basics (3): Store 翻訳
- Redux Basics (4): Data Flow 翻訳
- Redux Basics (5): Usage with React 翻訳
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はReactやDekuのようなライブラリとともに特によく動き、なぜなら状態の関数としてのUIを描画し、ReduxはActionへのレスポンスの中で状態の更新を発行するからです。
我々はシンプルなTodoアプリを構築するためにReactを使います。
React Reduxのインストール
React bindingsは標準でReduxを含みません。あなたは明示的にそれらをインストールする必要があります。
npm install --save react-redux
もしnpmを使わないならば、unpkgから最新のUMDビルド(developmentかproductionビルド)を取ってくると良いでしょう。もしあなたが<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
と呼ばれる特別な関数を定義する必要があります。例えば、VisibleTodoList
はTodoList
に渡すために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_TODO
Actionを呼び出すための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のコードが異なっているのと、コードにコメントがあったほうがわかりやすいので、コメント付き全コードを上げられたら上げようと思います。