GithubHelp home page GithubHelp logo

goodanthony / rails-webpack-react-flux Goto Github PK

View Code? Open in Web Editor NEW

This project forked from nambrot/rails-webpack-react-flux

0.0 1.0 0.0 228 KB

A Sample CRUD app in Rails/Webpack/React/Flux with server side rendering

Home Page: http://rails-react.nambrot.com/

Ruby 31.19% CoffeeScript 10.65% JavaScript 3.10% CSS 49.31% HTML 5.76%

rails-webpack-react-flux's Introduction

How to build a server-rendered web application with Rails, Webpack, React and Flux

Read the full blog post on my website and the demo at http://rails-react.nambrot.com/

I have build a very simple blog that offers the following features:

  • Fully server-rendered HTML, meaning you can actually use the application without JS enabled.
  • Minimization of unnecessary data fetching, as in embededing relevant data in the response as JSON and loading additional data if necessary
  • Once loaded, everything going forward is client-side rendered and cached, making it incredibly responsive.

I used Rails as my favorite web framework and combined it with React and Flux on the client side via Webpack to render your UI on the server. I want to give credits to others you have tried to make React work with Rails, namely Justin Gordon's Repo and Kevin Old

Most Rails/React/Flux examples out there are usually lacking in two departments:

  1. They are "just" TodoMVC, i.e. don't concern themselves with persistence to a real server by using localstorage.
  2. SSR examples with React usually only concern with spitting out HTML without addressing a data management strategy a la Flux. Most will return HTML but require a second rountrip to initialize the Stores. When they do, they usually load the whole collection in, which isn't so great.

I'll try to keep this as concise as possible in a step-by-step guide of how I approached the topic. I'm starting with a very basic Rails Blog that we will gradually "holy-grailify". The following links all points to commits with hopefully helpful commit messages.

1. Introducing Webpack and NPM

Love the asset pipeline to death, but the lack of true modules definitely gets noticable with larger amounts of client side code. We will be using Webpack to allow us to write modular code as well as use the great diversity of the NPM ecosystem, with easy compilation for client-side assets as well as hot-loading

2. Setup basic React and Flux

We are going to set the barebones of the Flux architecture without thinking too much about the server part, for now, we will just fetch everything on demand. That includes setting up the basic App structure

React-Router

# app.js
routes = (
  <Route name="app" path="/">
    <Route name="posts">
      <Route name="post" path=":postId">
        <DefaultRoute handler={PostShow} />
      </Route>
      <DefaultRoute handler={PostsIndex} />
    </Route>
    <DefaultRoute handler={PostsIndex} />
  </Route>
)

Flux with Flummox

  • Components
# components/posts/index.cjsx
Index = React.createClass
  displayName: "PostsIndex"
  render: ->
    <div>
      {
        @props.posts.map (post) ->
          <article key={post.get('id')}>
            <header>
              <h3>{post.get('title')}</h3>
            </header>
          </article>
        .toArray()
      }
    </div>

FluxIndex = React.createClass
  displayName: "WrappedPostsIndex"
  render: ->
    <FluxComponent connectToStores={
      posts: (store) => posts: store.getAllPosts(), didFetchAll: store.didFetchAll()
      }>
      <Index />
    </FluxComponent>
  • Actions
# actions/post.coffee
class PostActions extends Actions
  fetchAllPosts: ->
    axios.get('/posts', headers: acceptJSON)
    .then (posts) -> posts.data
  fetchPost: (id) ->
    axios.get("/posts/#{id}", headers: acceptJSON)
    .then (response) -> response.data

  createPost: (post) ->
    axios.post('/posts.json', post: post, headers: acceptJSON)
    .then (response) -> response.data
  updatePost: (post) ->
    axios.put("/posts/#{post.id}", post: post, headers:acceptJSON)
    .then (response) -> response.data
  • Store
# stores/post.coffee
class PostsStore extends Store
  constructor: (flux) ->
    super()

    postActionIds = flux.getActionIds('posts')
    @registerAsync postActionIds.fetchAllPosts, @startFetchingPosts, @fetchedAllPosts
    @registerAsync postActionIds.fetchPost, @startFetchingPost, @fetchedPost
    @registerAsync postActionIds.createPost, @startCreatePost, @createdPost
    @registerAsync postActionIds.updatePost, @startUpdatingPost, @updatedPost

    @state = getDefaultState()
    @flux = flux

The first step to server side rendering is to be able to deserialize data into the store for the client. This also avoids the inital request for data. React is also smart enough to not touch the DOM as the resulting HTML is identical.

# app.js
flux = new Flux()
flux.deserialize(window.serializedStoreState) if window.serializedStoreState
Router.run routes, Router.HistoryLocation, (Handler, state) ->
  handler = <FluxComponent flux={flux} render={ => <Handler  />}></FluxComponent>
  React.render(handler, document.getElementById("main"))
# stores/post.coffee
class PostsStore extends Store
  @deserialize: (serializedState) ->
    if serializedState
      posts: getPostsFromJSON(JSON.parse(serializedState.serializedPosts))
      didFetchAll: serializedState.didFetchAll
    else
      getDefaultState()

4. Add Server-Side Rendering

We are going to use a simple express server which will take the 1. route and 2. serializedState as parameters and simply return the HTML. The result is complete HTML pages being returned. In fact, you should now be able to navigate the page without the need for Javascript enabled, time-to-render on the client is faster, obvious SEO benefits. The downside is a "round-trip" to the express server

# server.coffee
renderToString = (route, serializedStoreState, callback) ->
  flux = new Flux()
  flux.deserialize(serializedStoreState)

  Router.run routes, route, (Handler, state) ->
    html = React.renderToString(<FluxComponent flux={flux} render={ => <Handler  />}></FluxComponent>)
    embeddedStoreState = "<script>"
    embeddedStoreState += "window.serializedStoreState = " + JSON.stringify(serializedStoreState)
    embeddedStoreState += "</script>"
    callback (html + embeddedStoreState)
# app/helpers/react_helper.rb
module ReactHelper
  def renderSerializedStoreState(state)
    component = HTTParty.post "http://localhost:3001", body: {path: request.path, serializedStoreState: state.to_json}.to_json, headers: {'Content-Type' => "application/json"}
    component.html_safe
  end
end

Conclusion

As you can see, building the "right" web application is not trivial, there is still a lot of decisions and configuration to be made. While the benefits are great, the cost may not be justifiable for many apps and should be carefully considered with the above trade-offs. I for one am excited for Ember's FastBoot that seems to be the least amount of effort to get the benefits.

rails-webpack-react-flux's People

Contributors

nambrot avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.