Spice Picks

Reduxの公式チュートリアルをきちんと勉強する-その②

May 02, 2019

その①の続きです。 その①と同様に、文中の引用文とソースコードは全てReduxの公式ドキュメントから引用しています。

Usage with React

ReduxをReact上で使えるようにします。 ReactとReduxを繋げるにはReact-Reduxを使います。 完全なガイドは公式ドキュメントを見るとして、基本を抑えていきます。 まずはnpm install --save react-reduxコマンドでインストールしときましょう。

Presentational and Container Components

React bindings for Redux separate presentational components from container components. This approach can make your app easier to understand and allow you to more easily reuse components.

ReactとReduxを繋げる際には、Reactのコンポーネントをpresentational componentscontainer componentsに分ける必要があります。 コンポーネントの役割をわかりやすくし、再利用性を高めるためです。 詳しい違いなどは記事中の表やDan先生の記事を読みましょう。

超簡単に言ってしまうと、presentational componentが

  • 見た目を記述する
  • データなや関数はpropsで受け取ったものを使用する

に対し、container componentは

  • 機能を記述する
  • Reduxのstateなどからデータを読み取る
  • presentational componentにデータなどを渡す

という感じ。

Designing Component

TODOアプリにおいて、どのようなコンポーネントが必要になるのかを考えています。 見た目を元にどんなPresentational Componentができるか考えて、それらのコンポーネントをReduxと繋げるたに必要なContainer Componentを構成します。

また、テキストを入力するためのボタンのコンポーネントも考えられています。

Implement Components

必要なコンポーネントを考えたら、実際に実装していきます。 見た目を記述するPresentational Componentから実装していますが、Reduxのデータと繋げる機能は持たないため、Reduxについて考慮せずに実装ができます。

Implementing Container Components

we suggest instead generating container components with the React Redux library’s connect() function, which provides many useful optimizations to prevent unnecessary re-renders.

自分のコードだけでContainer Componentを作成することもできるのですが、パフォーマンスの調整などを自動で行ってくれるconnect()関数の使用が推奨されています。 この関数を使用する場合、

  • Reduxのstoreのstateを、どのようにPresential Componentのpropsに渡すのか
  • Presential Component内で発火させたいdispatchの内容

を、自身で書く必要があります。 この条件を満たした、TODOアプリ内で要素を表示して、未完了・完了タスクなどの表示を切り替えるために使われるPresential Componentのソースコードは以下のように書かれています。

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

Passing the Store

上記の様にコンポーネントを作成した後、アプリのrootコンポーネントとなるAppコンポーネントにReduxのstoreを渡す形で記述してあるのが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'

const store = createStore(todoApp)

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

全てのコンポーネントがstoreをsubscribeするためには、全てのコンポーネントのpropsに必要な値を渡さなければいけません。 そのような恐ろしい行為をしなくて済むのがProviderコンポーネントです。

The option we recommend is to use a special React Redux component called to magically make the store available to all container components in the application without passing it explicitly.

上記のソースコードのように、rootとなるコンポーネントをProviderの子要素にし、Providerにstoreを渡せば、他のコンポーネントでもstoreを参照できるようになります。


Spice-Z

東京でフロントエンドエンジニアをやっているすぱいすのブログです。