diff --git a/Gemfile b/Gemfile
index b77cf4f..a08c24e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -55,3 +55,5 @@ end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+
+gem 'mini_racer', platforms: :ruby
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 4eceb93..d351168 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -81,6 +81,7 @@ GEM
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
+ libv8 (5.9.211.38.1-x86_64-darwin-16)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@@ -95,6 +96,8 @@ GEM
mime-types-data (3.2016.0521)
mini_mime (0.1.4)
mini_portile2 (2.2.0)
+ mini_racer (0.1.14)
+ libv8 (~> 5.9)
minitest (5.10.3)
multi_json (1.12.1)
nio4r (2.1.0)
@@ -199,6 +202,7 @@ DEPENDENCIES
coffee-rails (~> 4.2)
jbuilder (~> 2.5)
listen (>= 3.0.5, < 3.2)
+ mini_racer
puma (~> 3.7)
rails (~> 5.1.3)
react_on_rails!
diff --git a/Procfile.dev b/Procfile.dev
new file mode 100644
index 0000000..8af6663
--- /dev/null
+++ b/Procfile.dev
@@ -0,0 +1,4 @@
+web: rails s -p 3000
+
+# Next line runs a watch process with webpack
+client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack -w'
diff --git a/Procfile.dev-server b/Procfile.dev-server
new file mode 100644
index 0000000..038d37a
--- /dev/null
+++ b/Procfile.dev-server
@@ -0,0 +1,5 @@
+web: rails s -p 3000
+
+# Next line runs the webpack-dev-server
+# You can edit config/webpacker.yml to set HMR to true to see hot reloading
+client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack-dev-server'
diff --git a/app/controllers/hello_world_controller.rb b/app/controllers/hello_world_controller.rb
new file mode 100644
index 0000000..ad09c5e
--- /dev/null
+++ b/app/controllers/hello_world_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class HelloWorldController < ApplicationController
+ layout "hello_world"
+
+ def index
+ @hello_world_props = { name: "Stranger" }
+ end
+end
diff --git a/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js b/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js
new file mode 100644
index 0000000..62a1007
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.js
@@ -0,0 +1,8 @@
+/* eslint-disable import/prefer-default-export */
+
+import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
+
+export const updateName = (text) => ({
+ type: HELLO_WORLD_NAME_UPDATE,
+ text,
+});
diff --git a/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx b/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx
new file mode 100644
index 0000000..726e1c8
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx
@@ -0,0 +1,45 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+
+export default class HelloWorld extends React.Component {
+ static propTypes = {
+ name: PropTypes.string.isRequired, // this is passed from the Rails view
+ };
+
+ /**
+ * @param props - Comes from your rails view.
+ */
+ constructor(props) {
+ super(props);
+
+ // How to set initial state in ES6 class syntax
+ // https://facebook.github.io/react/docs/reusable-components.html#es6-classes
+ this.state = { name: this.props.name };
+ }
+
+ updateName = (name) => {
+ this.setState({ name });
+ };
+
+ render() {
+ return (
+
+
+ Hello, {this.state.name}!
+
+
+
+
+ );
+ }
+}
diff --git a/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js b/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js
new file mode 100644
index 0000000..70bc87e
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.js
@@ -0,0 +1,3 @@
+/* eslint-disable import/prefer-default-export */
+
+export const HELLO_WORLD_NAME_UPDATE = 'HELLO_WORLD_NAME_UPDATE';
diff --git a/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js b/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js
new file mode 100644
index 0000000..106b5e2
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.js
@@ -0,0 +1,13 @@
+// Simple example of a React "smart" component
+
+import { connect } from 'react-redux';
+import HelloWorld from '../components/HelloWorld';
+import * as actions from '../actions/helloWorldActionCreators';
+
+// Which part of the Redux global state does our component want to receive as props?
+const mapStateToProps = (state) => ({ name: state.name });
+
+// Don't forget to actually use connect!
+// Note that we don't export HelloWorld, but the redux "connected" version of it.
+// See https://github.com/reactjs/react-redux/blob/master/docs/api.md#examples
+export default connect(mapStateToProps, actions)(HelloWorld);
diff --git a/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js b/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js
new file mode 100644
index 0000000..313d188
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js
@@ -0,0 +1,15 @@
+import { combineReducers } from 'redux';
+import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
+
+const name = (state = '', action) => {
+ switch (action.type) {
+ case HELLO_WORLD_NAME_UPDATE:
+ return action.text;
+ default:
+ return state;
+ }
+};
+
+const helloWorldReducer = combineReducers({ name });
+
+export default helloWorldReducer;
diff --git a/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx b/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx
new file mode 100644
index 0000000..7d46b00
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+
+import configureStore from '../store/helloWorldStore';
+import HelloWorldContainer from '../containers/HelloWorldContainer';
+
+// See documentation for https://github.com/reactjs/react-redux.
+// This is how you get props from the Rails view into the redux store.
+// This code here binds your smart component to the redux store.
+const HelloWorldApp = (props) => (
+
+
+
+);
+
+export default HelloWorldApp;
diff --git a/app/javascript/bundles/HelloWorld/store/helloWorldStore.js b/app/javascript/bundles/HelloWorld/store/helloWorldStore.js
new file mode 100644
index 0000000..6a2b71c
--- /dev/null
+++ b/app/javascript/bundles/HelloWorld/store/helloWorldStore.js
@@ -0,0 +1,8 @@
+import { createStore } from 'redux';
+import helloWorldReducer from '../reducers/helloWorldReducer';
+
+const configureStore = (railsProps) => (
+ createStore(helloWorldReducer, railsProps)
+);
+
+export default configureStore;
diff --git a/app/javascript/packs/hello-world-bundle.js b/app/javascript/packs/hello-world-bundle.js
new file mode 100644
index 0000000..0531e67
--- /dev/null
+++ b/app/javascript/packs/hello-world-bundle.js
@@ -0,0 +1,8 @@
+import ReactOnRails from 'react-on-rails';
+
+import HelloWorldApp from '../bundles/HelloWorld/startup/HelloWorldApp';
+
+// This is how react_on_rails can see the HelloWorld in the browser.
+ReactOnRails.register({
+ HelloWorldApp,
+});
diff --git a/app/views/hello_world/index.html.erb b/app/views/hello_world/index.html.erb
new file mode 100644
index 0000000..820cc43
--- /dev/null
+++ b/app/views/hello_world/index.html.erb
@@ -0,0 +1,3 @@
+Hello World
+<%= react_component("HelloWorldApp", props: @hello_world_props, prerender: false) %>
+
diff --git a/app/views/layouts/hello_world.html.erb b/app/views/layouts/hello_world.html.erb
new file mode 100644
index 0000000..d9c08f9
--- /dev/null
+++ b/app/views/layouts/hello_world.html.erb
@@ -0,0 +1,12 @@
+
+
+
+ ReactOnRailsWithWebpacker
+ <%= csrf_meta_tags %>
+ <%= javascript_pack_tag 'hello-world-bundle' %>
+
+
+
+ <%= yield %>
+
+
diff --git a/config/initializers/react_on_rails.rb b/config/initializers/react_on_rails.rb
new file mode 100644
index 0000000..013728c
--- /dev/null
+++ b/config/initializers/react_on_rails.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+# See docs/basics/configuration.md for many more options
+
+ReactOnRails.configure do |config|
+ # This configures the script to run to build the production assets by webpack. Set this to nil
+ # if you don't want react_on_rails building this file for you.
+ config.npm_build_production_command = "RAILS_ENV=production bin/webpack"
+
+ ################################################################################
+ ################################################################################
+ # TEST CONFIGURATION OPTIONS
+ # Below options are used with the use of this test helper:
+ # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
+ ################################################################################
+
+ # If you are using this in your spec_helper.rb (or rails_helper.rb):
+ #
+ # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
+ #
+ # with rspec then this controls what yarn command is run
+ # to automatically refresh your webpack assets on every test run.
+ #
+ config.build_test_command = "RAILS_ENV=test bin/webpack"
+
+ ################################################################################
+ ################################################################################
+ # SERVER RENDERING OPTIONS
+ ################################################################################
+ # This is the file used for server rendering of React when using `(prerender: true)`
+ # If you are never using server rendering, you should set this to "".
+ # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size
+ # of the JS sent to the client. For the server rendering, React on Rails creates a pool of
+ # JavaScript execution instances which should handle any component requested.
+ #
+ # While you may configure this to be the same as your client bundle file, this file is typically
+ # different. You should have ONE server bundle which can create all of your server rendered
+ # React components.
+ #
+ config.server_bundle_js_file = "hello-world-bundle.js"
+end
diff --git a/config/routes.rb b/config/routes.rb
index 787824f..bbdd490 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,3 +1,4 @@
Rails.application.routes.draw do
+ get 'hello_world', to: 'hello_world#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
diff --git a/package.json b/package.json
index c7556b0..ec8c608 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,10 @@
"babel-preset-react": "^6.24.1",
"prop-types": "^15.5.10",
"react": "^15.6.1",
- "react-dom": "^15.6.1"
+ "react-dom": "^15.6.1",
+ "react-on-rails": "^9.0.0-beta.11",
+ "react-redux": "^5.0.6",
+ "redux": "^3.7.2"
},
"devDependencies": {
"webpack-dev-server": "^2.7.1"
diff --git a/yarn.lock b/yarn.lock
index 544305d..d6a45d2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2255,6 +2255,10 @@ hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+hoist-non-react-statics@^2.2.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -2389,7 +2393,7 @@ interpret@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
-invariant@^2.2.2:
+invariant@^2.0.0, invariant@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
dependencies:
@@ -2749,6 +2753,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
+lodash-es@^4.2.0, lodash-es@^4.2.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
+
lodash._baseassign@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
@@ -2864,7 +2872,7 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
-"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@~4.17.4:
+"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4172,6 +4180,21 @@ react-dom@^15.6.1:
object-assign "^4.1.0"
prop-types "^15.5.10"
+react-on-rails@^9.0.0-beta.11:
+ version "9.0.0-beta.11"
+ resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-9.0.0-beta.11.tgz#f6951129d7c2a67479281ca900859d5ac0aab978"
+
+react-redux@^5.0.6:
+ version "5.0.6"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946"
+ dependencies:
+ hoist-non-react-statics "^2.2.1"
+ invariant "^2.0.0"
+ lodash "^4.2.0"
+ lodash-es "^4.2.0"
+ loose-envify "^1.1.0"
+ prop-types "^15.5.10"
+
react@^15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
@@ -4267,6 +4290,15 @@ reduce-function-call@^1.0.1, reduce-function-call@^1.0.2:
dependencies:
balanced-match "^0.4.2"
+redux@^3.7.2:
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
+ dependencies:
+ lodash "^4.2.1"
+ lodash-es "^4.2.1"
+ loose-envify "^1.1.0"
+ symbol-observable "^1.0.3"
+
regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
@@ -4863,6 +4895,10 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
+symbol-observable@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
+
tapable@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"