Rails 5.0 と React、Redux、Ajaxを組み合わせて掲示板アプリを作ってみよう

Rails 5.0 と React、Redux、Ajaxを組み合わせて掲示板アプリを作ってみよう

Rails 5.0 と React、Redux、Ajaxを組み合わせて掲示板アプリを作ってみよう

Rails 5.0 と React、Redux、Ajaxを組み合わせて掲示板アプリを作ってみよう へのコメントはまだありません

はじめに

Reduxを勉強して、ようやく大筋を理解し、Railsとの連携方法も分かったので、
Rails+React+Redux+Ajaxの組み合わせでちょっとした掲示板を作ってみました。

Rails+React+Redux+Ajaxの組み合わせで必要なノウハウはだいたい組み込まれていると思います。
ここで紹介するソースはGitHubのここで公開していますので自由に利用してください。

対象とする読者は Railsはそこそこ知っている。Reactも勉強した。Reduxも勉強した、もしくは勉強中である
という方が対象です。

Rails + React + Reduxの環境を整える

これに関しては書き出すと長くなるので、この記事を参照して準備してください。
Todoサンプルを動かす必要はないので、とりあえず RailsとReactとReduxのインストールと連携の部分の作業を行ってください。

Railsのインストールやnode.js npm のインストールなどは他に山ほど丁寧に書かれたサイトがあるので、それらを参照してください。

掲示板アプリ作成開始

さて、いよいよ掲示板アプリの作成開始です。

完成形の動作イメージはこんな感じです。
左に掲示板一覧がリストアップされていて、右に投稿を行う場所と投稿一覧が表示されている感じです。

redux-21

Railsプロジェクト作成

[bash]
$ cd ~/
$ mkdir workspace
$ cd workspace
$ rails new rails_redux_board
$ cd rails_redux_board

[/bash]

これ以降の RailsとReact、Reduxの組み合わせの環境を構築する手順はこちらの記事(Rails5.0 に React、Reduxを導入して、Redux本家のTodoサンプルを動かす)を参照にして頑張ってみてください。

とりあえず、Redux本家のTodoが動かせたらOKです。手間を省きたければTodoアプリのコピペはしなくて結構です。

components.js編集

app/assets/javascripts/components.js

board.jsはアプリのエントリーポイントとなるJSファイルです。ここに定義したクラス名(const宣言)を
windowの下にセット(変数として公開)してやる。
(意味はいまいち理解できていないのですが、必須みたいです)

全て消して以下の4行を追記。Todoアプリを移植していない場合は、Todosの行は不要

[bash]
window.React = require(‘react’);
window.ReactDOM = require(‘react-dom’);
window.Todos = require(‘./todos.js’);
window.Board = require(‘./board.js’);
[/bash]

React+Reduxの掲示板のエントリーポイントとなるJSファイルを作成

app/assets/javascripts/board.js
[bash]
import React, { Component } from ‘react’
import { render } from ‘react-dom’
import { Provider } from ‘react-redux’
import { createStore } from ‘redux’
import rootReducer from ‘./reducers/root’
import App from ‘./components/App’

const Board = ({}) => {
return (



)
}

export default Board
[/bash]

すべてのReducerをまとめる親Reducer作成

app/assets/javascripts/reducers/root.js

[bash]
import {combineReducers} from “redux”

const rootReducer = combineReducers({})

export default rootReducer

[/bash]

アプリケーションのトップ階層となるApp.jsを作成

app/assets/javascripts/components/App.js
[bash]
import React from “react”

const App = ({}) => {
return (

This is Board Application with Rails,React,Redux

)
}

export default App
[/bash]

Railsでコントローラーとビューを作成

[bash]
$ rails g controller board index

[/bash]

ビュー編集

ビューを編集して、掲示板のReactコンポーネントを作成します。
app/views/board/index.html.erb

[bash]

Board#index

Find me in app/views/board/index.html.erb

<%= react_component('Board') %>

[/bash]

ここまでで動作確認

これで最低限のところは出来たので、Railsサーバーを起動して、動作確認。

http://localhost:3000/board/index

こんな感じのイメージが表示されれば成功。
redux-22

モデル作成、サンプルデータ作成

Railsのモデルを作成し、サンプルデータをSeedファイル(CSV)として読み込める状態にしましょう。

モデルの作成
[bash]
$ rails g model board title:string description:text
$ rails g model user name:string sex:integer age:integer
$ rails g model comment board_id:integer user_id:integer comment:text
[/bash]

マイグレート

[bash]
$ rake db:migrate
[/bash]

出来上がったイメージはこんな感じ。
rails-1

Seedデータの作成

何もないところからデータ投稿までつくり上げるのには時間がかかるので、シード(seed)データを
作っておいて、まずはそのデータを読んで、一覧表示することぐらいから始めましょう。

db/csv/boards.csv
[bash]
1,ReactJSを学ぼう,あいうえお
2,ReactJS と Rails連携,かきくけこ
3,ReactJS 発展編,さしすせそ
4,jQuery入門,たちつてと
5,jQuery得意な人集まれ,なにぬねの
6,AngularJS入門,はひふへほ
7,vue.jsってなんぞ,まみむめも
[/bash]

db/csv/users.csv

[bash]
1,花子,2,23
2,太郎,1,30
3,幸次郎,1,15
4,浩輝,1,40
5,大樹,1,35
6,すず,2,28
7,雅代,2,38

[/bash]

db/csv/comments.csv

[bash]
“1”,”1″,”2″,”ReactはFacebookが開発しているUI(MV*フレームワークでいうViewのようなもの)に特化したJavaScriptライブラリです。 ”
“2”,”1″,”3″,”大きな特徴としてVirtual DOM(仮想DOM)と呼ばれるレンダリング機構がそなわっており、Webページの表示を従来の .”
“3”,”1″,”3″,”公式サイトに、「A JavaScript library for building user interfaces」とあるように、React.jsはUIを構築するためのライブラリです。”
“4”,”1″,”3″,”フレームワークでなくあくまでUIを構築するだけのライブラリで、MVCでいうところのVのみの機能を提供します。”
“5”,”1″,”7″,”最近フロントエンドでfacebook/reactをずっと使っている。世界的には一部のエンジニアの間で流行っているのだが、国内だとqiitaのタグ等を見てもどうも少ない。”
“6”,”2″,”7″,”はじめにreact-railsという、ReactをAsset Pipelineに乗せて使えるようにしてくれるruby gemsがある。 この記事では、これを使用してReactの公式tutorialを進めていく。”
“7”,”2″,”4″,”既存のRailsプロジェクトの中でReact.jsを利用する機会があったので、その時にやったことについてまとめてみます。 私自身は普段RailsのサーバサイドとCoffeeScriptが中心で、最近のJavaScript開発環境についてあまりキャッチアップでき”
“8”,”2″,”5″,”RailsでSingle-Page Applicationをつくるときの自分のやり方をまとめてみます。 Gem 「JavaScriptで書かれたReactのコンポーネントからHTMLを生成する」というのをRubyでやるために、RubyのV8エンジン実装で ..”
“9”,”2″,”2″,”react-railsというReact.jsをRailsに簡単に統合できるgemを使い、React.jsについて説明します。 次のような画面をReact.jsで実装し、Reactとサーバ(Rails)間でメッセージ一覧の取得や作成をできるようにします。 ソースコードはこちらです。”
“10”,”3″,”2″,”最近のモダンなウェブフレームワークと言えば、React+Reduxですよね。でも、なんか難しそうとか、ReactってPHPみたいにViewにロジック混ざりそうとか感じて尻込みしていませんか?それはただの誤解かもしれません。React+Reduxはそんなに難易度の高い .”
“11”,”3″,”1″,”玉石混合状態にあったFluxのフレームワークも、ここ最近ではReduxが首一つ抜け出したような感じとなっています。自分はFacebook/Flux派ではありましたが、先月発売された『WEB+DB PRESS vol.92』に掲載されていた”
“12”,”3″,”6″,” React.jsとReduxを組み合わせた構成で、Webサイトを作ってみるチュートリアル(React+Redux入門)です。第一回目は、GitHub上のコードが読めるようになることを目標に解説しています。”
“13”,”3″,”3″,”目的 ReduxとES6への入門。 React.jsを(ようやく)触る機会が出て、情報量の多いFluxxorとCoffeeScriptで入門してた。 ”
“14”,”7″,”3″,”私たちはなぜReactではなくVue.jsを選んだのか Qwintryチームは最近、既存のすべてのプロジェクトのフロントエンドをVue.jsに移行しはじめました。”
“15”,”6″,”4″,”AngularJS(アンギュラージェイエス)、または Angular は、Googleと個人や企業のコミュニティによって開発されている、完全にJavaScriptで書かれたオープンソースのフロントエンドWebアプリケーションフレームワークである。MIT Licenseでライセンスされた”
“16”,”4″,”4″,”今後のWebデザイナーは、デザイン・htmlコーディング+その他の強みを求められる世の中になっていきます。今回はその強みとなりうる「jQuery」についてご説明いたします。”
“17”,”4″,”2″,”今回はjQueryの基本的な書き方として、これから「jQueryを覚えて、ブイブイ言わせてやるぜ!」と新しいスキルを身につけたいデザイナーさんには丁度いい内容かと思います。”
“18”,”5″,”2″,”今年2016年6月9日、ついに正式版がリリースされた jQuery 3。メジャーバージョンアップしたjQuery 3では、従来のバージョンから、いったい何が変わったのかを2回に分けて解説します。”
“19”,”5″,”1″,” jQueryはWeb業界の発展に大いに役立ってきました。しかし、ネイティブのJavaScriptが高度化し、かつマーケットシェアの縮小した古いブラウザをサポートする必要の薄れた現代において、jQueryを本当に使う必要があるでしょうか?必要性と”
“20”,”4″,”1″,”Webデザイナーやマークアップエンジニアのための超コンパクトなjQuery講座ができました。JavaScriptの予備知識は不要。Web制作に必要な要点だけを解説します。 (1/6)”
“21”,”7″,”6″,”双方向データバインディングに特化したVue.jsは、シンプルで学習コストも低いといわれます。第1回目はVue.jsがフレームワークとして、どんな強みをもっているのか”
“22”,”7″,”7″,”Angular、Reactと並んで海外で人気が高まっている「Vue.js」。ReactとAngularの開発経験がある著者がVue.jsをサンプルコードつきのチュートリアルを通じて特徴をまとめて解説します。2017年、新しく学び始めるきっかけにどうぞ。”

[/bash]

Seedデータをデータベースに反映するスクリプト

db/seeds.rbを編集

[bash]
require ‘csv’

CSV.foreach(‘db/csv/boards.csv’) do |row|
Board.create!(id: row[0], title: row[1], description: row[2])
end

CSV.foreach(‘db/csv/users.csv’) do |row|
User.create!(id: row[0], name: row[1], sex: row[2], age: row[3])
end

CSV.foreach(‘db/csv/comments.csv’) do |row|
Comment.create!(id: row[0], board_id: row[1], user_id: row[2], comment: row[3])
end

[/bash]

Seedデータの投入

[bash]
$ rake db:seed
[/bash]

完成イメージからパーツ構成を導き出す

いよいよReact+Reudxの本論に入ります。
まずは、こんな感じのものができればいいなという理想型です。

redux-21
これをパーツに分解するとこんな感じ
redux-23

Reactっぽい発想からReduxの発想にシフトする

Reactの場合は単純にこのパーツイメージでコンポーネントを作っていけばよかったのですが、
React+Reduxの場合は、ちょっと発想を追加する必要があります。
基本的に上で表示したパーツは全て必要です。

Reduxでは Reactで言うステートやイベントを組み込みたいコンポーネントにひとつ
ラッパーコンポーネントを作ります。これ重要です。

必要なステートを考える

ここでまず考えなければならないのが、なにをステートとして保持するかということです。
これはReactの考え方と全く同じでOKです。

今回の場合は以下になると思います。

  • 現在アクティブな掲示板
  • 掲示板に投稿されたコメント一覧

掲示板一覧、ユーザー一覧は初期値を与えたあとは固定なので、ステートではありません。

発生するイベントを考える

掲示板で発生するイベントは以下の2つが考えられます。

  • 掲示板選択
  • コメント投稿

コンポーネントの上のラッパーコンポーネントは?

ステートとイベントをどうするかが決まったので、ラッパーコンポーネントを挟む位置を考えます。
掲示板選択のイベントはBoardListクラスから発生するので、その上にラッパーをかぶせます。
ラッパー名は決まりはありませんが、ラッパーするクラスの前に「C」をつけたクラスとします。
ですので、この場合は「CBoardList」となります。

掲示板の投稿を行うのはCommentAddクラスですので、このラッパーは「CCommentAdd」クラスとなります。

最後にイベントは発生しませんが、掲示板が選択された時に、掲示板に投稿されたコメント一覧を
リフレッシュする必要がありますので、コメント一覧を保持している CommentListクラスのラッパーとして
「CCommentList」クラスも必要となります。

Appコンポーネントから、アプリケーションの表示の大枠を作る

難しいかもしれませんが、我慢してください。

app/assets/javascripts/components/App.js
[bash]
import React from “react”
import CBoardList from “../containers/CBoardList”
import CCommentAdd from “../containers/CCommentAdd”
import CCommentList from “../containers/CCommentList”

const App = ({}) => {
return (


)
}

export default App
[/bash]

app/assets/javascripts/containers/CBoardList.js

[bash]
import React from “react”
import {connect} from “react-redux”
import BoardList from “../components/BoardList”

const mapStateToProps = (state) =>{
return {
}
}

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

const CBoardList = connect(mapStateToProps,mapDispatchToProps)(BoardList)

export default CBoardList

[/bash]

app/assets/javascripts/containers/CCommentAdd.js
[bash]
import React from “react”
import {connect} from “react-redux”
import CommentAdd from “../components/CommentAdd”

const mapStateToProps = (state) => {
return {
}
}

const mapDispatchToProps = (dispatch) => {
return {
}
}

const CCommentAdd = connect(mapStateToProps,mapDispatchToProps)(CommentAdd)

export default CCommentAdd

[/bash]

app/assets/javascripts/containers/CCommentList.js
[bash]
import React from “react”
import {connect} from “react-redux”
import CommentList from “../components/CommentList”

const mapStateToProps = (state) => {
return {

}
}

const CCommentList = connect(mapStateToProps,null)(CommentList)

export default CCommentList

[/bash]

ラッパーコンポーネントからconnectされたコンポーネント(本体)を作る

connect(mapStateToProps,mapDispatchToProps)(xxxxxxx) として作られた
xxxxxxxxのコンポーネントを作ります。

app/assets/javascripts/components/BoardList.js

[bash]
import React from “react”

const BoardList = ({}) => {
return (

This is Board List

)
}

export default BoardList

[/bash]

app/assets/javascripts/components/CommentAdd.js
[bash]
import React from “react”

const CommentAdd = ({}) => {

return (

This is Comment Add Area

)
}

export default CommentAdd
[/bash]

app/assets/javascripts/components/CommentList.js
[bash]
import React from “react”

const CommentList = ({}) => {

return (

This is Comment List

)
}

export default CommentList

[/bash]

アプリケーションで利用するCSS作成

とりあえず全て貼り付けておきます。単純にコピペしてください。

app/assets/stylesheets/board.scss

[bash]
// Place all the styles related to the board controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
div.left-side{
float: left;
width: 400px;
}

div.right-side {
float: left;
width: 600px;
}

div.bline{

label.id{
display: inline-block;
width: 40px;
}

label.title{
display: inline-block;
width: 280px;
}

label.active{
background-color: #f68c8c;
}
}

div.cadd {
padding: 20px;
border: 3px solid #c86a87;
height: 80px;

textarea {
width: 450px;
float: left;
}
button {
float: left;
width: 80px;
height: 60px;
}
}

div.clist {
clear: both;
margin-top: 30px;
border-bottom: 1px solid #cccccc;
}

div.cline {
padding: 5px;
border-top: 1px solid #cccccc;
border-right: 1px solid #cccccc;
border-left: 1px solid #cccccc;
clear: both;
height: 90px;

label.name {
display: inline-block;
width: 100px;
}

label.sex {
display: inline-block;
width: 50px;
font-size: 9px;
color: #555555;
}

label.age {
display: inline-block;
width: 50px;
font-size: 9px;
color: #555555;
}

div.icon {
float: left;
width: 50px;
}
div.comment{
float: left;
width: 500px;
padding: 5px;
padding-left: 20px;

}

}

[/bash]

ここまでで動作確認

これで最低限のところは出来たので、Railsサーバーを起動して、動作確認。

http://localhost:3000/board/index

こんな感じのイメージが表示されれば成功。
redux-25

ステート(Reducer)を考える

既に考えているステートは以下のものです。
これら1つ1つをReducerとして作りましょう。

  • 現在アクティブな掲示板
  • 掲示板に投稿されたコメント一覧

掲示板一覧、ユーザー一覧は 今回は初期値を与えたあとは変動しないので、ステートとは考えません。

最初はよくわからないかもしれませんが・・・こんなものだと思って我慢してください。

app/assets/javascripts/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]

app/assets/javascripts/reducers/comments.js
[bash]
const comments = (state=[], action) => {
if (action.comments == undefined) {
return []
}
else {
return action.comments
}
}

export default comments

[/bash]

発生するイベント(ActionCreator)を考える

掲示板で発生するイベントは以下の2つでした。これをもとにActionCreatorを作成しましょう。
これも理解しがたいと思いますが、我慢してください。

  • 掲示板選択
  • コメント投稿

app/assets/javascripts/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]

rootReducerの調整

Reducerが出揃ったのでルートReducerを調整します。

app/assets/javascripts/reducers/root.js
[bash]
import {combineReducers} from “redux”
import sel_board from “./sel_board”
import comments from “./comments”

const rootReducer = combineReducers({
sel_board: sel_board,
comments: comments
})

export default rootReducer
[/bash]

掲示板一覧を表示する

アプリケーションの骨格が出来上がってきて、Reducerも出揃ったので、
いよいよ掲示板一覧を表示してみましょう。

Railsでデータの読み込み

コントローラーに以下のソースを追記(indexの中の2行)

app/controllers/board_controller.rb

[bash]
class BoardController < ApplicationController def index @boards = Board.all.order("created_at DESC") @users = User.all.order("id") end end [/bash]

ビュー調整

読み込んだ値をReactコンポーネントに引数として与えます。
app/views/board/index.html.erb

[bash]

Board#index

Find me in app/views/board/index.html.erb

<%= react_component('Board', {boards: @boards, users: @users}) %>

[/bash]

エントリーポイント調整

アプリケーションのエントリーポイント、アプリケーションのトップ階層を調整します。

app/assets/javascripts/board.js

[bash]
import React, { Component } from ‘react’
import { render } from ‘react-dom’
import { Provider } from ‘react-redux’
import { createStore } from ‘redux’
import rootReducer from ‘./reducers/root’
import App from ‘./components/App’

const Board = ({boards, users}) => {
return (



)
}

export default Board

[/bash]

app/assets/javascripts/components/App.js
[bash]
import React from “react”
import CBoardList from “../containers/CBoardList”
import CCommentAdd from “../containers/CCommentAdd”
import CCommentList from “../containers/CCommentList”

const App = ({boards, users}) => {
return (


)
}

export default App

[/bash]

掲示板一覧のラッパー「CBoardList」、掲示板一覧「BoardList」を調整

app/assets/javascripts/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
}
}

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

const CBoardList = connect(mapStateToProps,mapDispatchToProps)(BoardList)

export default CBoardList

[/bash]

app/assets/javascripts/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]

掲示板の一行を表す「BoardLine」を作成

[bash]
import React from “react”

const BoardLine = ({id, title,active, onClick}) => {
let cssact = active ? “active” : “”

return (



)
}

export default BoardLine
[/bash]

ここまでで動作確認

これで最低限のところは出来たので、Railsサーバーを起動して、動作確認。

http://localhost:3000/board/index

こんな感じのイメージが表示されれば成功。
redux-26

コメント投稿エリア作成

ラッパー「CCommentAdd」、投稿エリア本体「CommentAdd」を調整します。

app/assets/javascripts/containers/CCommentAdd.js
[bash]
import React from “react”
import {connect} from “react-redux”
import CommentAdd from “../components/CommentAdd”
import {comment_add, comment_read} from “../actions/index”

const mapStateToProps = (state, ownProps) => {
return {
users: ownProps.users,
sel_board: state.sel_board
}
}

const mapDispatchToProps = (dispatch) => {
return {
onAddComment: (sel_board, user, cmt) => {
}
}
}

const CCommentAdd = connect(mapStateToProps,mapDispatchToProps)(CommentAdd)

export default CCommentAdd

[/bash]

app/assets/javascripts/components/CommentAdd.js
[bash]
import React from “react”

const CommentAdd = ({users, sel_board, onAddComment}) => {
let ulist = []
let inpCom,inpUser

users.map((u) => {
ulist.push()
})

return (



日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

Back to Top