Skip to content

Commit

Permalink
Merge pull request #277 from shakacode/shared-redux-store
Browse files Browse the repository at this point in the history
Moving header to React with shared store
  • Loading branch information
justin808 authored Jul 20, 2016
2 parents ec43d8e + 0ecedf2 commit 47e4852
Show file tree
Hide file tree
Showing 28 changed files with 279 additions and 87 deletions.
2 changes: 0 additions & 2 deletions app/assets/javascripts/application_non_webpack.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// All webpack assets in development will be loaded via webpack dev server

// turbolinks comes from npm and is listed in webpack.client.base.config.js

//= require rails_startup
5 changes: 0 additions & 5 deletions app/assets/javascripts/rails_startup.js

This file was deleted.

19 changes: 18 additions & 1 deletion app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
class PagesController < ApplicationController
include ReactOnRails::Controller
before_action :set_comments

def index
# NOTE: The below notes apply if you want to set the value of the props in the controller, as
# compared to he view. However, it's more convenient to use Jbuilder from the view. See
# compared to the view. However, it's more convenient to use Jbuilder from the view. See
# app/views/pages/index.html.erb:20
#
# <%= react_component('App', props: render(template: "/comments/index.json.jbuilder"),
Expand All @@ -20,10 +21,15 @@ def index
# respond_to do |format|
# format.html
# end

redux_store("routerCommentsStore", props: comments_json_string)
render_html
end

# Declaring no_router and simple to indicate we have views for them
def no_router
redux_store("commentsStore", props: comments_json_string)
render_html
end

def simple
Expand All @@ -34,4 +40,15 @@ def simple
def set_comments
@comments = Comment.all.order("id DESC")
end

def comments_json_string
render_to_string(template: "/comments/index.json.jbuilder",
locals: { comments: Comment.all }, format: :json)
end

def render_html
respond_to do |format|
format.html
end
end
end
30 changes: 5 additions & 25 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,16 @@
<%= csrf_meta_tags %>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="http://www.shakacode.com">ShakaCode</a>
</div>

<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><%= link_to "React/Redux/Router Demo", root_path %></li>
<li><%= link_to "React/Redux", no_router_path %></li>
<li><%= link_to "Simple React", simple_path %></li>
<li><%= link_to "Classic Rails", comments_path %></li>
<li><%= link_to "Source on Github", "https://github.com/shakacode/react-webpack-rails-tutorial" %></li>
<li><%= link_to "Tutorial Article", "http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/" %></li>
<li><%= link_to "Forum Discussion", "http://forum.shakacode.com/t/fast-rich-client-rails-development-with-webpack-and-the-es6-transpiler/82/22" %></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<%= react_component "NavigationBarApp" %>

<div class="container">
<%= yield %>
</div>

<!-- This is a placeholder for ReactOnRails to know where to render the store props for
client side hydration -->
<%= redux_store_hydration_data %>

</body>
</html>
4 changes: 2 additions & 2 deletions app/views/pages/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
</h3>
<%= render "header" %>

<%= react_component('RouterApp', props: render(template: "/comments/index.json.jbuilder"),
prerender: true, raise_on_prerender_error: true, id: "RouterApp-react-component-0") %>
<!--Note, pre-rendering set in /config/initializers/react_on_rails.rb -->
<%= react_component('RouterApp', id: "RouterApp-react-component-0") %>
4 changes: 2 additions & 2 deletions app/views/pages/no_router.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
<a href="https://github.com/shakacode/react_on_rails">react_on_rails gem</a>)</h2>
<%= render "header" %>

<%= react_component('App', props: render(template: "/comments/index.json.jbuilder"),
prerender: true) %>
<!--Note, pre-rendering set in /config/initializers/react_on_rails.rb -->
<%= react_component('App') %>
1 change: 1 addition & 0 deletions app/views/pages/simple.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
</ul>
<hr/>

<!-- This component is super simple! So no redux, no prerendering. -->
<%= react_component('SimpleCommentScreen', props: {}, prerender: false) %>
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default class CommentScreen extends BaseComponent {
{this._renderNotification()}
<div>
<CommentBox
pollInterval={10000}
pollInterval={60000}
data={data}
actions={actions}
ajaxCounter={data.get('ajaxCounter')}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, {PropTypes} from 'react';

const href = 'https://github.com/shakacode/react_on_rails/blob/master/README.md#multiple-react-' +
'components-on-a-page-with-one-store';
const CommentsCount = (props) => (
<li>
<a id='js-comment-count' href={href}>
Comments: {props.commentsCount}
</a>
</li>
);

CommentsCount.propTypes = {
commentsCount: PropTypes.number.isRequired,
};

export default CommentsCount;
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, {PropTypes} from 'react';
import ReactOnRails from 'react-on-rails';
import classNames from 'classnames';
import CommentsCount from './CommentsCount';
import * as paths from '../../constants/paths';

const NavigationBar = (props) => {
const { commentsCount, pathname } = props;

return (
<nav className="navbar navbar-default" role="navigation">
<div className="container">
<div className="navbar-header">
<button
type="button"
className="navbar-toggle"
data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1"
>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"/>
<span className="icon-bar"/>
<span className="icon-bar"/>
</button>
<a className="navbar-brand" href="http://www.shakacode.com">ShakaCode</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
<li className={classNames({ active: (pathname === paths.ROUTER_PATH) })}>
<a href={paths.ROUTER_PATH}>React Router Demo</a>
</li>
<li className={classNames({ active: (pathname === paths.NO_ROUTER_PATH) })}>
<a href={paths.NO_ROUTER_PATH}>React Demo</a>
</li>
<li className={classNames({ active: (pathname === paths.SIMPLE_REACT_PATH) })}>
<a href={paths.SIMPLE_REACT_PATH}>Simple React</a>
</li>
<li className={classNames({ active: (pathname === paths.RAILS_PATH) })}>
<a href={paths.RAILS_PATH}>Classic Rails</a>
</li>
<li>
<a href={
'https://github.com/' +
'shakacode/react-webpack-rails-tutorial'
}>
Source on Github
</a>
</li>
<li>
<a href={
'http://www.railsonmaui.com/' +
'blog/2014/10/03/integrating' +
'-webpack-and-the-es6-transpiler' +
'-into-an-existing-rails-project/'
}>Tutorials</a>
</li>
<li>
<a href={
'http://forum.shakacode.com/' +
't/fast-rich-client-rails-development' +
'-with-webpack-and-the-es6-transpiler/82/22'
}>Forum</a>
</li>
{commentsCount && CommentsCount({ commentsCount })}
</ul>
</div>
</div>
</nav>
);
};

NavigationBar.propTypes = {
commentsCount: PropTypes.number,
pathname: PropTypes.string.isRequired,
};

export default NavigationBar;
4 changes: 4 additions & 0 deletions client/app/bundles/comments/constants/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const ROUTER_PATH = '/';
export const NO_ROUTER_PATH = '/no-router';
export const SIMPLE_REACT_PATH = '/simple';
export const RAILS_PATH = '/comments';
37 changes: 37 additions & 0 deletions client/app/bundles/comments/containers/NavigationBarContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import NavigationBar from '../components/NavigationBar/NavigationBar';
import * as commentsActionCreators from '../actions/commentsActionCreators';
import BaseComponent from 'libs/components/BaseComponent';

function stateToProps(state) {
// Which part of the Redux global state does our component want to receive as props?
if (state.$$commentsStore) {
return {
commentsCount: state.$$commentsStore.get('$$comments').size,
pathname: state.railsContext.pathname,
};
} else {
return { };
}
}

class NavigationBarContainer extends BaseComponent {
static propTypes = {
commentsCount: PropTypes.number.isRequired,
pathname: PropTypes.string.isRequired,
};

render() {
const { commentsCount, pathname } = this.props;

return (
<NavigationBar {...{ commentsCount, pathname }} />
);
}
}

// Don't forget to actually use connect!
export default connect(stateToProps)(NavigationBarContainer);
3 changes: 3 additions & 0 deletions client/app/bundles/comments/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import commentsReducer, { $$initialState as $$commentsState } from './commentsReducer';
import railsContextReducer, { initialState as railsContextState } from './railsContextReducer';

export default {
$$commentsStore: commentsReducer,
railsContext: railsContextReducer,
};

export const initialStates = {
$$commentsState,
railsContextState,
};
5 changes: 5 additions & 0 deletions client/app/bundles/comments/reducers/railsContextReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const initialState = {};

export default function railsContextReducer(state = initialState, action = null) {
return state;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';
import { Provider } from 'react-redux';

import createStore from '../store/commentsStore';
import NonRouterCommentsContainer from '../containers/NonRouterCommentsContainer';

export default props => {
const store = createStore(props);
export default (_props, _railsContext) => {
const store = ReactOnRails.getStore('commentsStore');

return (
<Provider store={store}>
<NonRouterCommentsContainer />
Expand Down
7 changes: 4 additions & 3 deletions client/app/bundles/comments/startup/ClientRouterApp.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Compare to ../ServerRouterApp.jsx
import React from 'react';
import { Provider } from 'react-redux';
import ReactOnRails from 'react-on-rails';
import { Router, browserHistory } from 'react-router';
import createStore from '../store/routerCommentsStore';
import routes from '../routes/routes';
import { syncHistoryWithStore } from 'react-router-redux';

export default (props, location) => {
const store = createStore(props);
export default (_props, _railsContext) => {
const store = ReactOnRails.getStore('routerCommentsStore');

// Create an enhanced history that syncs navigation events with the store
const history = syncHistoryWithStore(
Expand Down
36 changes: 36 additions & 0 deletions client/app/bundles/comments/startup/NavigationBarApp.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Top level component for client side.
// Compare this to the ./ServerApp.jsx file which is used for server side rendering.

import React from 'react';
import ReactOnRails from 'react-on-rails';
import NavigationBar from '../components/NavigationBar/NavigationBar';
import NavigationBarContainer from '../containers/NavigationBarContainer';
import { Provider } from 'react-redux';
import * as paths from '../constants/paths';

/*
* Export a function that returns a ReactComponent, depending on a store named SharedReduxStore.
* This is used for the client rendering hook after the page html is rendered.
* React will see that the state is the same and not do anything.
*/
export default (_props, railsContext) => {
// This is where we get the existing store.
const stores = ReactOnRails.stores();
const { pathname } = railsContext;
let store;
if (pathname === paths.ROUTER_PATH) {
store = ReactOnRails.getStore('routerCommentsStore', false);
} else if (pathname === paths.NO_ROUTER_PATH) {
store = ReactOnRails.getStore('commentsStore', false);
} else {
return (
<NavigationBar {...{ pathname }} />
);
}

return (
<Provider store={store}>
<NavigationBarContainer />
</Provider>
);
};
14 changes: 0 additions & 14 deletions client/app/bundles/comments/startup/ServerApp.jsx

This file was deleted.

4 changes: 2 additions & 2 deletions client/app/bundles/comments/startup/ServerRouterApp.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Compare to ../ClientRouterApp.jsx
import React from 'react';
import { Provider } from 'react-redux';
import { match, RouterContext } from 'react-router';

import createStore from '../store/commentsStore';
import routes from '../routes/routes';

export default (props, railsContext) => {
const store = createStore(props);
const store = ReactOnRails.getStore('routerCommentsStore');

let error;
let redirectLocation;
Expand Down
Loading

0 comments on commit 47e4852

Please sign in to comment.