はじめに
React+Redux+RailsでSPAアプリを作ることになったので、あれこれ調べたのでメモです。
SPAというと、当然ですが、1つのページであれこれできちゃうものを作らなきゃいけないわけで・・・
そうなるとコンテンツ制御の仕組みが必要です。そこで登場するのがReact-Router-Dom。
で、これ単体では使い方簡単なんですが、RailsとReduxを絡めて動作させる方法が分からなかったので
あれこれ調べて、なんとか動くところまでたどり着いたので、メモを公開します。
React-Routerは古いようで、React-Router-Domにしてみました。。
ドキュメントが超少なくてまいりました。
完成図はこんな感じ。
青いところがメニューで、クリックすると、下のコンテンツがReact-Routerで入れ替わります。
ソースコードはGitHubで公開しています。利用方法は、記事の末尾を参照ください。
Railsプロジェクト作成
[bash]
$ rails new react_router_with_redux
$ cd react_router_with_redux/
[/bash]
Gemを調整して、bundle install
[bash]
$ nano Gemfile
[/bash]
[bash]
gem ‘therubyracer’, platforms: :ruby
gem ‘react-rails’
gem ‘browserify-rails’
[/bash]
[bash]
$ bundle install
[/bash]
application.rbを編集
作った ES6(JavaScriptのバージョン)のソースをES5に自動変換するための設定を追記
config.browserify_rails の行を追加
config/application.rb
[bash]
require_relative ‘boot’
require ‘rails/all’
# Require the gems listed in Gemfile, including any gems
# you’ve limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module RailsRedux
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
+ config.browserify_rails.commandline_options = '-t babelify'
end
end
[/bash]
Rails に Reactインストール
[bash]
$ rails g react:install
[/bash]
application.jsの編集
app/assets/javascripts/application.js
・Reactは npmでインストールしたものを使うのでカット。
・require_tree . もカット
変更前
[bash]
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require_tree .
[/bash]
変更後
[bash]
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react_ujs
//= require components
[/bash]
React、Redux、etc…の準備(node_modules)
React、Redux、React-Routerその他諸々を npm を使ってインストールします。
[bash]
$ npm init -y
$ npm install –save-dev browserify browserify-incremental babelify babel-preset-es2015 babel-preset-react
$ npm install –save react react-dom react-redux redux redux-thunk react-router-dom
[/bash]
.babelrc ファイルをRailsのルートフォルダに作成
[bash]
{
“presets”: [“es2015”, “react”]
}
[/bash]
Reduxの標準的なフォルダを作成
[bash]
$ cd app/assets/javascripts/
$ mkdir actions containers reducers
[/bash]
components.js編集
app/assets/javascripts/components.js
アプリ名は 「MyApp」とします。
myapp.jsはアプリのエントリーポイントとなるJSファイルです。ここに定義したクラス名(const宣言)を
windowの下にセット(変数として公開)してやる。
変更前
[bash]
//= require_tree ./components
[/bash]
変更後
[bash]
window.React = require(‘react’);
window.ReactDOM = require(‘react-dom’);
window.MyApp = require(‘./myapp.js’);
[/bash]
Reduxでカウンターアプリ、+静的ページをひたすら作る
ここ以降、しばらくは、Reduxでカウンターのインクリメント、デクリメントできるアプリを作ります。
加えて、静的ページもいくつか作ります。
何も考えずにソースをコピーしていってやってください・・・
app/assets/javascripts/actions/index.js
[bash]
export const counter_increment = () => {
return {
type: “INCREMENT”
}
}
export const counter_decrement = () => {
return {
type: “DECREMENT”
}
}
[/bash]
app/assets/javascripts/components/About.js
[bash]
import React from “react”
const About = () => {
return (
This is About
)
}
export default About
[/bash]
app/assets/javascripts/components/Contact.js
[bash]
import React from “react”
const Contact = () => {
return (
This is Contact Page
)
}
export default Contact
[/bash]
app/assets/javascripts/components/Counter.js
[bash]
import React from “react”
const Counter = ({counter, onIncrement, onDecrement}) => {
return (
onIncrement()}>
Count Up
onDecrement()}>
Count Down
Counter = {counter}
)
}
export default Counter
[/bash]
app/assets/javascripts/components/Top.js
[bash]
import React from “react”
const Top = () => {
return (
This is Top Page
)
}
export default Top
[/bash]
ここ withRouter という react-router-dom のクラスを使ってます。
app/assets/javascripts/containers/CCounter.js
[bash]
import {connect} from “react-redux”
import { withRouter } from ‘react-router-dom’
import {counter_increment, counter_decrement} from “../actions/index”
import Counter from “../components/Counter”
const mapStateToProps = (state) => {
return {
counter: state.counter
}
}
const mapDispatchToProps = (dispatch) => {
return {
onIncrement: () => {
dispatch(counter_increment())
},
onDecrement: () => {
dispatch(counter_decrement())
}
}
}
const CCounter = withRouter(connect(mapStateToProps, mapDispatchToProps)(Counter))
export default CCounter
[/bash]
app/assets/javascripts/reducers/counter.js
[bash]
const counter = (state = 0, action) => {
switch (action.type) {
case “INCREMENT”:
return state + 1
case “DECREMENT”:
return state – 1
default:
return state
}
}
export default counter
[/bash]
app/assets/javascripts/reducers/root.js
[bash]
import {combineReducers} from “redux”
import counter from “./counter”
const rootReducer = combineReducers({
counter: counter
})
export default rootReducer
[/bash]
ここまでで、とりあえず、カウンターアプリと、静的ページは出来上がり。
これ以降、アプリのエントリーポイント、React-Router周りに必要な処理を記載します。
アプリのエントリーポイント
app/assets/javascripts/myapp.js
[bash]
import React from “react”
import {createStore} from “redux”
import rootReducer from “./reducers/root”
import Root from “./components/Root”
class MyApp extends React.Component{
render(){
let store = createStore(rootReducer)
return (
)
}
}
export default MyApp
[/bash]
アプリケーションRoot
app/assets/javascripts/components/Root.js
[bash]
import React from “react”
import {Provider} from “react-redux”
import { BrowserRouter as Router, Route} from “react-router-dom”
import App from “./App”
import Top from “./Top”
import About from “./About”
import Contact from “./Contact”
import CCounter from “../containers/CCounter”
const Root = ({store}) => {
return (
}/>
)
}
export default Root
[/bash]
アプリケーションのトップページ
app/assets/javascripts/components/App.js
[bash]
import React from “react”
import {NavLink} from “react-router-dom”
class App extends React.Component {
render(){
return (
This is React Redux ReactRouter App
Top
Counter
About
Contact
{this.props.children}
)
}
}
export default App
[/bash]
Rails でコントローラーとビューを作成
[bash]
$ rails g controller myapp index
[/bash]
ビューを調整
ビューを調整して、SPAアプリのコンポーネントを出力します。
app/views/myapp/index.html.erb
[bash]
<%= react_component("MyApp") %>
[/bash]
ルーティングの調整
React-Routerで ページ移動するたびに myapp/xxxx へ移動しますが、
このパスを myapp#index に割り当ててやる。
config/routes.rb
[bash]
Rails.application.routes.draw do
get ‘myapp/index’
get ‘myapp/*path’, to: ‘myapp#index’
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
[/bash]
デザイン調整
app/assets/stylesheets/myapp.scss
[bash]
// Place all the styles related to the first controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
div.app{
ul.menus{
margin: 0;
padding: 0;
li{
float: left;
list-style: none;
width: 100px;
background-color: #346299;
padding: 15px;
padding-top: 20px;
border-right: 1px solid #cccccc;
a {
color: #ffffff;
}
a.active{
color: #efe250;
}
}
}
div.clear{
clear: both;
}
[/bash]
動作確認
[bash]
$ rails s
[/bash]
http://localhost:3000/myapp/index
以下のようなページが表示されれば、成功。
カウンターのページで、ボタンをクリックすると、カウンターが増減していたら、
Redux+React-Routerの組み合わせがうまく機能しているということになります。
プログラムからページ(Route)を切り替える
ボタンのクリックアクションなどでページを切り替えたい場合があると思います。
正式な方法かわかりませんが、以下の方法で出来ました。
Counterページから、Contactページに移動したらOKです。
プロパティに historyというのが存在するみたいで、それを利用します。
app/assets/javascripts/components/Counter.js
[bash]
import React from “react”
const Counter = ({counter, onIncrement, onDecrement, history}) => {
return (
onIncrement()}>
Count Up
onDecrement()}>
Count Down
Counter = {counter}
history.push(‘/myapp/contact’)}>
Go To Contact
)
}
export default Counter
[/bash]
ボタンクリック
Contactページヘ移動
GitHubソースの利用方法
このソースはGitHubで公開していますので、ご自由に利用ください。
インストール手順
[bash]
$ git clone https://github.com/h-mito/react_router_with_redux
$ cd react_router_with_redux
$ bundle install
$ npm install
[/bash]
動作確認
[bash]
$ rails s
[/bash]
ブラウザーで以下のURLへアクセス
http://localhost:3000/myapp/index
以上
Leave a comment