はじめに
ここ3週間ほど死ぬほどReactとReduxやって、あれこれ分かるようになったので、これからReact+Reduxで開発を始めたい人向けに記事を書いてみます。
公式サイトの英語記事や、それを翻訳してくれているようなサイトを見てもそれなりに分かるのですが・・・
なんだかんだ言って難しい。
Reactの理解はなんとか出来ても、Reduxが特に難しい。
欲張りなんですが、とにかく両方をサーッと理解して、さっさとアプリ組みたいよという方の助けになれればと・・・
考えていく手順
- 画面イメージ
- 画面を構築するためのコンポーネント階層図
- ステート
- イベント
基本的にこの4つを考えればOKです。
既に別の記事で書いている RailsとReactとReduxを組み合わせた掲示板アプリの画面を例にとって説明します。
まずは作るアプリの画面イメージを頭の中で構築してください。紙に絵を書いてみたりするといいですね。
この例の場合は、画面は至ってシンプル。画面左側に掲示板一覧がリストアップされていて、その掲示板のどれかを選択すると、画面右側に掲示板に投稿されたコメント一覧が列挙される。
画面右上では、掲示板に新たにコメントを投稿できる。こんなもの。

この画面を構成するパーツをReact風に考えてみると、ざっとこんな感じ。
これが「画面を構築するためのコンポーネント階層図」。

次、ステート。
この画面イメージを見て、変動しそうなものを考える。
ざっと考えると掲示板一覧と、コメント一覧、アクティブな掲示板、ユーザー一覧 ぐらい。
ユーザー一覧がなんで必要かというと、このアプリでは説明を簡単にするために、ログイン処理など設けずに、投稿する際に「自分は誰々ですよ」と選択して、コメントを投稿するために必要です。
で、上記の4つが本当にステートか考える。
よく考えてみると、掲示板一覧は追加できる機能がないので、初期値固定のまま。ユーザー一覧も初期値固定のまま。
よってこの2つはステートとは考えない。
じゃあ、ステートとして残るのは
の2つだけ。
次、イベント。
これはすぐ解ると思います。
- 掲示板一覧からどれかを選択する
- 掲示板にコメントを投稿する
この2つだけ。
どうやってアプリを組み上げていくか
- 画面イメージ
- 画面を構築するためのコンポーネント階層図
- ステート
- イベント
考える手順でこれはできたので、どこからか手を付けていきます。
画面から先に作りたくなるかもしれませんが、あれこれややこしくなるので
アプリのトップになるApp.js ぐらいだけ作って、そこにHelloWorldぐらいを出すだけにして、
先にデータ構造とイベントの部分を考えましょう。たぶんこれがRedux風な考え方。
データ構造とはつまりステートのこと。データ構造が変化するときはどんな時かというとイベントが発生した時。
となるので、まずイベント一覧(ActionCreator)を作りましょう。
actions/index.js
[bash]
export const board_select = (id) => {
return {
type: “BOARD_SELECT”,
id: id
}
}
export const comment_read = (comments) => {
return {
type: “COMMENT_READ”,
comments: comments
}
}
[/bash]
ここでは2つのfunctionを作って、そのconst(function)をexport してます。
functionの戻り値が actionオブジェクト。
ちょっと難しくなってきましたね。とりあえず、actionオブジェクトにはTypeプロパティを
必ず作り、そこにどんなイベントか識別できる文字列をセットしておきます。
そして、type以外のプロパティはイベントで発生したあとに変化する値を列挙します。
BOARD_SELECT つまり、掲示板が選択されたら、アクティブな掲示板IDが変わるので、
id をセットしておくというような感じ。
次、イベントが発生したあと、実際にデータ(public変数みたいなイメージ)を変更するところを作ります。
その場所がreducerです。
reducers/sel_board.js
[bash]
const sel_board = (state=-1, action)=> {
switch (action.type) {
case “BOARD_SELECT”:
return action.id
default:
return state
}
}
export default sel_board
[/bash]
これも1つ const(function)を定義したもの。functionの引数の1つめは state、2つめが action。
これはほぼ固定でOK。で、stateには 初期値をセットしておく。
掲示板アプリでは起動直後、アクティブな掲示板がないので、初期値は「-1」。
で、functionの中身はというと、 action.typeにセットされた文字列が、自分が反応したいイベントか判断して、
もしそうであれば、イベント後に変更したい値を作って返すというもの。
今回はアクティブな掲示板が変更された というイベントが来るので、当然新しく選ばれた掲示板のIDを返してあげる
というものです。
ちょっと難しくなりますが、
reducerでは、stateに入っている現在の値は参照するのみとし、新しい結果を returnしてやるのがお約束です。
次に画面を構築するコンポーネントを見ていきましょう。
掲示板一覧のコンポーネント「BoardList」の例です。
初期化の引数に {datas, selb, onBoardClick} が来ています。
datas は掲示板一覧のデータの配列。
selbはアクティブな掲示板のID。
onBoardClickは掲示板を選択した時に反応してほしい functionがセットされています。
イベントの処理はここでは書かないのがRedux風。
components/BoardList.js
[bash]
import React from “react”
import BoardLine from “./BoardLine”
const BoardList = ({datas, selb, onBoardClick}) => {
let list = []
datas.map((b) => {
let active = (b.id == selb)
list.push( onBoardClick(b.id)}
/>)
})
return (
{list}
)
}
export default BoardList
[/bash]
ここが一番難しい、コンポーネントのラッパー(container)クラス。
BoardList.js をラップする CBoardList.js
containers/CBoardList.js
[bash]
import React from “react”
import {connect} from “react-redux”
import {board_select, comment_read} from “../actions/index”
import BoardList from “../components/BoardList”
const mapStateToProps = (state, ownProps) =>{
return {
datas: ownProps.boards,
selb: state.sel_board
}
}
const mapDispatchToProps = (dispatch) => {
return {
onBoardClick: (id) => {
dispatch(board_select(id))
$.ajax({
type: “GET”,
url: “/board/readComments/” + id,
async: false,
dataType: “json”,
data: “name=John&location=Boston”,
success: function(data, dataType){
if (data.status == true){
//alert(data.rows.length);
if (data.rows.length > 0){
dispatch(comment_read(data.rows))
}
else{
dispatch(comment_read([]))
}
}
},
error :function(XMLHttpRequest, textStatus, errorThrown){
}
});
}
}
}
const CBoardList = connect(mapStateToProps,mapDispatchToProps)(BoardList)
export default CBoardList
[/bash]
とりあえず、以下の3つをおさえればよい。
const mapStateToProps = (state, ownProps) =>{
const mapDispatchToProps = (dispatch) => {
const ラッパーコンポーネント = connect(mapStateToProps,mapDispatchToProps)(画面を構築する実態コンポーネント)
1つ1つ説明していくと。
mapStateToProps では、storeのstateが直接参照できるので、実態コンポーネントのプロパティにセットしてあげたい
値をここでセットする。
mapDispatchToProps は実態のコンポーネントで発生したイベントの処理を記述するところ。
Ajax等で何らかデータを取ってくるなりして、撮ってきた結果を引数にして、
dispatch(イベント名(データ)) としてやる。
この例では
dispatch(comment_read(data.rows))
ここで何が起こっているかというと、comment_read という ActionCreatorを変化する値を引数にして呼び出す。
呼びだされたActionCreatorは、引数で渡ってきた値を使って、storeに新しい値を返す。
するとstoreにセットされた値が変わるので、Reactが自動的にその値を使っている部分を再描画してくれる。
というもの。
基本はこれだけ。これを繰り返していけば、アプリが組めていきます。
なんかよくわからんなぁと、最初はもやもやするかもしれませんが、慣れてくれば、手に取るように分かるようになりますので、
頑張ってみてください。
GitHubにソース公開してます。
ご自由にご利用ください。
https://github.com/h-mito/rails_redux_board
おわり
Leave a comment