Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React On Rails

React On Rails

React is a powerful JS (I know!) library for building user interfaces. Our goal is to build a blogging app with markdown editor and live HTML preview powered by Ruby on Rails and React. In doing so we will learn what makes React more than yet another JS library blowing up Hacker News.

Associated Github repo: https://github.com/ConnerMan/ReactOnRails

Avatar for Conner Wingard

Conner Wingard

September 08, 2015
Tweet

Other Decks in Programming

Transcript

  1. GOALS • Build a simple blog powered by React views

    • Bridge the gap between Rails and React • See what all the fuss on HN is about…
  2. var PostList = React.createClass({ render: function() { var createPost =

    function(post){ return <Post post={post} />; }; return( <div className='post-list'> {this.props.posts.map(createPost)} </div> ); } }); <%= react_component('PostList', {posts: @posts}) %> app/views/posts/index.html.erb app/assets/javascripts/components/PostList.jsx
  3. var PostList = React.createClass({ render: function() { var createPost =

    function(post){ return <Post post={post} />; }; return( <div className='post-list'> {this.props.posts.map(createPost)} </div> ); } }); <%= react_component('PostList', {posts: @posts}) %> app/views/posts/index.html.erb app/assets/javascripts/components/PostList.jsx
  4. var PostList = React.createClass({ render: function() { var createPost =

    function(post){ return <Post post={post} />; }; return( <div className='post-list'> {this.props.posts.map(createPost)} </div> ); } }); <%= react_component('PostList', {posts: @posts}) %> app/views/posts/index.html.erb app/assets/javascripts/components/PostList.jsx
  5. … var createPost = function(post){ return <Post post={post} />; };

    … app/assets/javascripts/components/PostList.jsx var Post = React.createClass({ render: function() { var post = this.props.post; return( <div className='post'> <h1 className='post-title'>{post.title}</h1> <div className='post-body'>{post.contents}</div> </div> ); } }); app/assets/javascripts/components/Post.jsx
  6. … var createPost = function(post){ return <Post post={post} />; };

    … app/assets/javascripts/components/PostList.jsx var Post = React.createClass({ render: function() { var post = this.props.post; return( <div className='post'> <h1 className='post-title'>{post.title}</h1> <div className='post-body'>{post.contents}</div> </div> ); } }); app/assets/javascripts/components/Post.jsx
  7. … var createPost = function(post){ return <Post post={post} />; };

    … app/assets/javascripts/components/PostList.jsx var Post = React.createClass({ render: function() { var post = this.props.post; return( <div className='post'> <h1 className='post-title'>{post.title}</h1> <div className='post-body'>{post.contents}</div> </div> ); } }); app/assets/javascripts/components/Post.jsx
  8. … var createPost = function(post){ return <Post post={post} />; };

    … app/assets/javascripts/components/PostList.jsx var Post = React.createClass({ render: function() { var post = this.props.post; return( <div className='post'> <h1 className='post-title'>{post.title}</h1> <div className='post-body'>{post.contents}</div> </div> ); } }); app/assets/javascripts/components/Post.jsx
  9. … var createPost = function(post){ return <Post post={post} />; };

    … app/assets/javascripts/components/PostList.jsx var Post = React.createClass({ render: function() { var post = this.props.post; return( <div className='post'> <h1 className='post-title'>{post.title}</h1> <div className='post-body'>{post.contents}</div> </div> ); } }); app/assets/javascripts/components/Post.jsx
  10. class PostsController < ApplicationController … def new @post = Post.new

    end def create @post = Post.new(post_params) if @post.save redirect_to posts_path else render action: :new end end … private def post_params params.require(:post).permit( :title, :contents ) end end app/controllers/posts_controller.rb
  11. var PostForm = React.createClass({ getInitialState: function() { return({post: this.props.post ||

    {}}); }, handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }}); }, render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true' method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <div className='form-group'> <input name='post[title]' ref='title' className='form-control' type='text' placeholder='Title' onChange={this.handleChange} value={this.state.post.title} /> </div> <div className='form-group'> <textarea name='post[contents]' ref='contents' className='form-control' placeholder="Contents" rows='5' onChange={this.handleChange} value={this.state.post.contents} /> </div> <input className='btn btn-primary' type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } }); PostForm.jsx
  12. PostForm.jsx - getInitialState() var PostForm = React.createClass({ // Return an

    object representing the initial state of the component getInitialState: function() { return({ post: this.props.post || {} }); } //... });
  13. var PostForm = React.createClass({ // Return an object representing the

    initial state of the component getInitialState: function() { return({ post: this.props.post || {} }); } //... }); // Later we can access our state inside of our component like so... this.state.post // > {title: "My New Post", contents: "Thoughts about things.."} this.state.post.title // > "My New Post" // State is mutable... We can change it by calling `setState` this.setState({post: { title:"My new post (revised)", contents: " My New Post contents" }) PostForm.jsx - getInitialState()
  14. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  15. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  16. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  17. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  18. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  19. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  20. //.. render: function() { return( <div className='new-post'> <form action={this.props.action} remote='true'

    method='POST'> <input type='hidden' name='authenticity_token' value={this.props.authenticity_token} /> <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> <input type='submit' value='Save'/> </form> <Post post={this.state.post}/> </div> ); } //.. PostForm.jsx - render()
  21. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  22. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  23. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  24. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  25. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  26. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  27. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  28. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  29. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> //... ); } PostForm.jsx - handleChange()
  30. handleChange: function() { this.setState({post: { title: React.findDOMNode(this.refs.title).value, contents: React.findDOMNode(this.refs.contents).value }});

    }, render: function() { return( //... <input type='text' name='post[title]' ref='title' onChange={this.handleChange} value={this.state.post.title} /> <textarea name='post[contents]' ref='contents' onChange={this.handleChange} value={this.state.post.contents} /> // We can now simply render our existing Post. // Passing the current state will give us a live preview <Post post={this.state.post}/> ); } PostForm.jsx - handleChange()
  31. JUST THE UI • Just the V in MVC •

    Makes no assumptions about the rest of your stack • Libraries exist for integrating React into most tech stacks, Rails, Node, Meteor
  32. VIRTUAL DOM • React abstracts the DOM away from the

    developer to provide simplicity and speed • Re-rendering components is made inexpensive by a DOM diffing algorithm • Isomorphic views (React components can be rendered client side or server side!)
  33. ONE WAY DATA FLOW • Data flows one-way in React

    views/apps • Easier to reason about then two-way data binding
  34. REACT NATIVE • Use the same React components your web

    app in your native iPhone/Android apps • Same tool for UI across projects. Learn once, write everywhere