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

Reactjs on Rails (slides only)

Reactjs on Rails (slides only)

An introductory talk to React.js for Rails developers. Covers high-level concepts, implementation, and languages. Includes audience questions.

Ad9e927bce4bd0519631296b5af7af83?s=128

Michael Chan

April 24, 2015
Tweet

Transcript

  1. R E A C T. J S O N R

    A I L S
  2. None
  3. michael chan

  4. @chantastic

  5. @chantastic

  6. None
  7. Launched 2006 on Rails 1.13

  8. So our approach to JavaScript has been:

  9. So our approach to JavaScript has been: Yes; write some

  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. T U R B O L I N K S

    3 . 0
  21. None
  22. None
  23. B U T W E W E R E S

    T I L L W R I T I N G A L O T O F J AVA S C R I P T
  24. None
  25. None
  26. None
  27. None
  28. C O M P O N E N T S

  29. M Y G O A L

  30. MY GOAL T H AT Y O U C O

    U L D S H I P A R E A C T C O M P O N E N T I N A N H O U R
  31. STRETCH GOAL Y O U D O N ’ T

    M A K E T H E S A M E D U M B M I S TA K E S W E D I D
  32. A R E A C T P R I M

    E R
  33. J AVA S C R I P T L I

    B R A RY B Y FA C E B O O K
  34. F O R C R E AT I N G

    U S E R I N T E R FA C E S
  35. U S I N G C O M P O

    N E N T S
  36. C O M P O N E N T S

    D O 3 T H I N G S :
  37. • R E N D E R • R E

    C E I V E P R O P S • M A I N TA I N S TAT E C O M P O N E N T S D O 3 T H I N G S :
  38. T H I N K PA R T I A

    L W I T H L O C A L S
  39. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul>
  40. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul>
  41. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> • Welcome To New York • Blank Space • Style • Out of the Woods • Shake It Off • I Wish You Would • Bad Blood • Wildest Dreams • How You Get the Girl • This Love • I know Places • Clean
  42. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul>
  43. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul>
  44. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> _songs.html.erb
  45. <ul> <% @album.songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> _songs.html.erb
  46. <%= render "songs" %> <ul> <% @album.songs.each do |song| %>

    <li><%= song.name %></li> <% end %> </ul> _songs.html.erb
  47. <%= render "songs" %> <ul> <% @album.songs.each do |song| %>

    <li><%= song.name %></li> <% end %> </ul> _songs.html.erb
  48. <%= render "songs" %> <ul> <% songs .each do |song|

    %> <li><%= song.name %></li> <% end %> </ul> _songs.html.erb
  49. _songs.html.erb <%= render "songs" %> <ul> <% songs.each do |song|

    %> <li><%= song.name %></li> <% end %> </ul>
  50. <%= render partial: "songs", locals: { songs: @album.songs } %>

    _songs.html.erb <ul> <% songs.each do |song| %> <li><%= song.name %></li> <% end %> </ul>
  51. <%= render partial: "songs", locals: { songs: @album.songs } %>

    _songs.html.erb <ul> <% songs.each do |song| %> <li><%= song.name %></li> <% end %> </ul>
  52. <%= render partial: "songs", locals: { songs: @album.songs } %>

    _songs.html.erb <ul> <% songs.each do |song| %> <li><%= song.name %></li> <% end %> </ul> 1989 • Welcome To New York • Blank Space • Style • Out of the Woods • Shake It Off • I Wish You Would • Bad Blood • Wildest Dreams • How You Get the Girl • This Love • I know Places • Clean RED • State Of Grace • Red • Treacherous • I Knew You Were Trouble • All Too Well • 22 • I Almost Do • We Are Never Ever Getting Back Together • Stay Stay Stay • The Last Time • … Speak Now • Mine • Sparks Fly • Back To December • Speak Now • Dear John • Mean • The Story Of Us • Never Grow Up • Enchanted • Better Than Revenge • Innocent • Haunted • Last Kiss
  53. <% @albums.each do |album| %> <% end %> index.html.erb <%=

    render partial: "songs", locals: { songs: @album.songs } %>
  54. <% @albums.each do |album| %> <h2><%= album.title %></h2> <%= render

    partial: "songs", locals: { songs: album.songs } %> <% end %> index.html.erb <%= render partial: "songs", locals: { songs: @album.songs } %>
  55. <% @albums.each do |album| %> <h2><%= album.title %></h2> <%= render

    partial: "songs", locals: { songs: album.songs } %> <% end %> index.html.erb <%= render partial: "songs", locals: { songs: @album.songs } %>
  56. Y O U K N O W T H I

    S
  57. L E T ’ S D O I T I

    N J AVA S C R I P T W I T H R E A C T
  58. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %>
  59. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %>
  60. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %> _songs.html.erb
  61. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %> _songs.html.erb window.Songs
  62. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %>
  63. <%= render partial: "songs", locals: { songs: @album.songs } %>

    <%= react_component: "Songs", { songs: @album.songs } %> props
  64. P R O P S A R E I M

    M U TA B L E * kinda
  65. L E T ’ S D E F I N

    E A C O M P O N E N T
  66. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs
  67. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({}); render() {
  68. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({ render() { return ; } });
  69. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({ render() { return <ul></ul>; } });
  70. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({ render() { return <ul>{this.props.songs}</ul>; } });
  71. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({ render() { return <ul>{this.props.songs.map()}</ul>; } });
  72. <ul> <% songs.each do |song| %> <li><%= song.name %></li> <%

    end %> </ul> var Songs = React.createClass({ render() { var createItem = () => return <ul>{this.props.songs.map(createItem)}</ul>; } });
  73. var Songs = React.createClass({ render() { var createItem = (song)

    => ; return <ul>{this.props.songs.map(createItem)}</ul>; } }); <ul> <% songs.each do |song| %> <li><%= song.name %></li> <% end %> </ul>
  74. var Songs = React.createClass({ render() { var createItem = (song)

    => <li>{song.name}</li>; return <ul>{this.props.songs.map(createItem)}</ul>; } }); <ul> <% songs.each do |song| %> <li><%= song.name %></li> <% end %> </ul>
  75. L E T ’ S P R A C T

    I C E !
  76. R E A L - T I M E C

    O M M E N T S F O R T H E 1 5 - M I N U T E B L O G
  77. None
  78. <% @post.comments.each do |comment| %> <%= comment.body %> <hr />

    <% end %>
  79. <% @post.comments.each do |comment| %> <%= comment.body %> <hr />

    <% end %>
  80. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  81. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  82. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  83. comment.js.jsx var Comment = React.createClass({ render() { return <div></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  84. comment.js.jsx var Comment = React.createClass({ render() { return <div><hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  85. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  86. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= comment.body %> <hr /> <% end %>
  87. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <% end %>
  88. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment: comment.body } %> <% end %>
  89. comment.js.jsx var Comment = React.createClass({ render() { return <div>{this.props.comment}<hr/></div>; }

    }); <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment: comment.body } %> <% end %>
  90. None
  91. None
  92. None
  93. None
  94. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %>
  95. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %>
  96. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <ul>{this.props.comments.map(createItem)}</ul>; } });
  97. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <ul>{this.props.comments.map(createItem)}</ul>; } });
  98. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <ul>{this.props.comments.map(createItem)}</ul>; } });
  99. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div></div>; } });
  100. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments}</div>; } });
  101. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map()}</div>; } });
  102. comments.js.jsx var Comments = React.createClass({ render() { var createItem =

    () => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } }); <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment: comment.body } %> <% end %>
  103. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } });
  104. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment />; return <div>{this.props.comments.map(createItem)}</div>; } });
  105. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment= />; return <div>{this.props.comments.map(createItem)}</div>; } });
  106. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } });
  107. <% @post.comments.each do |comment| %> <%= react_component "Comment", { comment:

    comment.body } %> <% end %> comments.js.jsx var Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } });
  108. <%= react_component "Comment", { comment: comment.body } %> comments.js.jsx var

    Comments = React.createClass({ render() { var createItem = ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } });
  109. comments.js.jsx var Comments = React.createClass({ render() { var createItem =

    ({body}) => <Comment comment={body} />; return <div>{this.props.comments.map(createItem)}</div>; } }); <%= react_component "Comments", { comments: @post.comments } %>
  110. None
  111. None
  112. None
  113. None
  114. G E T T O T H E R E

    A L - T I M E B I T
  115. < C o m m e n t s /

    > C O M P O N E N T
  116. < C o m m e n t / >

    C O M P O N E N T
  117. < C o m m e n t s C

    o n t a i n e r / > C O M P O N E N T
  118. • F E T C H C O M M

    E N T S • W H I C H C O M P O N E N T R E N D E R S T H E M < C o m m e n t s C o n t a i n e r / > C O M P O N E N T
  119. T H I N K O F I T L

    I K E A R A I L S C O N T R O L L E R
  120. <%= react_component "CommentsContainer", commentsPath: comments_post_path(@post) %>

  121. <%= react_component "CommentsContainer", commentsPath: comments_post_path(@post) %>

  122. <%= react_component "CommentsContainer", commentsPath: comments_post_path(@post) %>

  123. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });;
  124. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  125. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  126. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  127. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  128. var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000); },

    fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  129. comments_container.js.jsx var CommentsContainer = React.createClass({});

  130. comments_container.js.jsx var CommentsContainer = React.createClass({ render() { return ; }

    });
  131. comments_container.js.jsx var CommentsContainer = React.createClass({ render() { return <Comments />;

    } });
  132. comments_container.js.jsx var CommentsContainer = React.createClass({ render() { return <Comments comments={[]}

    />; } });
  133. comments_container.js.jsx var CommentsContainer = React.createClass({ render() { return <Comments comments={[]}

    />; } }); state
  134. comments_container.js.jsx var CommentsContainer = React.createClass({ render() { return <Comments comments={this.state.comments}

    />; } });
  135. comments_container.js.jsx var CommentsContainer = React.createClass({ getInitialState() { }, render() {

    return <Comments comments={this.state.comments} />; } });
  136. comments_container.js.jsx var CommentsContainer = React.createClass({ getInitialState() { return { comments:

    [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  137. comments_container.js.jsx var CommentsContainer = React.createClass({ getInitialState() { return { comments:

    [] }; }, render() { return <Comments comments={this.state.comments} />; } });
  138. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  139. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  140. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  141. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />; <%= react_component "CommentsContainer", commentsPath: comments_post_path(@post) %>
  142. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  143. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  144. comments_container.js.jsx var CommentsContainer = React.createClass({ fetchComments() { $.getJSON( this.props.commentsPath, (data)

    => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] }; }, render() { return <Comments comments={this.state.comments} />;
  145. comments_container.js.jsx var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); }, fetchComments()

    { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() { return { comments: [] };
  146. comments_container.js.jsx var CommentsContainer = React.createClass({ componentWillMount() { this.fetchComments(); setInterval(this.fetchComments, 1000);

    }, fetchComments() { $.getJSON( this.props.commentsPath, (data) => this.setState({comments: data}); ); }, getInitialState() {
  147. None
  148. None
  149. None
  150. None
  151. S O H O W M U C H D

    O Y O U N E E D T O K N O W T O B E E F F E C T I V E ?
  152. <%= react_component "Greeting" %> 1

  153. <%= react_component "Greeting", name: "Bob" %> 1

  154. var Greeting = React.createClass({ render() { return <h2>Hi {this.props.name}!</h2>; }

    }); 2
  155. var Greeting = React.createClass({ render() { return <h2>Hi {this.props.name}!</h2>; }

    }); 3
  156. propTypes 4

  157. propTypes: { name: React.PropTypes.string } 4

  158. propTypes: { name: React.PropTypes.string } 4

  159. propTypes: { name: React.PropTypes.string } 4

  160. getDefaultProps() 5

  161. getDefaultProps() { return { name: "guest" } } 5

  162. getDefaultProps() { return { name: "guest" } } Think of

    this as ||= in Ruby 5
  163. this.setState() 6

  164. this.setState() The REFRESH button of React 6

  165. getInitialState() { return { comments: [] } } 7

  166. componentWillMount() 8

  167. componentWillMount() { $.getJSON('/comments.json', handleComments) } 8

  168. componentWillMount() componentWillUnmount() 9

  169. componentWillMount() { window.addEventListener('scroll', …); }, componentWillUnmount() { window.removeEventListener('scroll', …); }

    9
  170. componentWillMount() { window.addEventListener('scroll', …); }, componentWillUnmount() { window.removeEventListener('scroll', …); }

    9
  171. componentWillMount() { source.addEventListener('new-message', …); }, componentWillUnmount() { source.removeEventListener('new-message', …); }

    9
  172. componentWillMount() { channel.bind('new-message', …); }, componentWillUnmount() { channel.bind('new-message', …); }

    9
  173. How many APIs 9

  174. None
  175. None
  176. None
  177. None
  178. I M P L E M E N TAT I

    O N
  179. R E A C T- R A I L S

    G E M
  180. gem react-rails run rails g react:install R E A C

    T- R A I L S G E M
  181. rails g react:component Greeting R E A C T- R

    A I L S G E M
  182. var Greeting = React.createClass({ render() { return <div />; }

    }); R E A C T- R A I L S G E M
  183. rails g react:component Greeting name:string R E A C T-

    R A I L S G E M
  184. var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string }, render()

    { return ( <div> <div>Name: {this.props.name}</div> </div> ); } }); R E A C T- R A I L S G E M
  185. R A I L S - A S S E

    T S
  186. gem 'sdoc', '~> 0.4.0', group: :doc group :development, :test do

    gem 'byebug' gem 'web-console', '~> 2.0' gem 'spring' end gem 'react-rails', '~> 1.0' source 'https://rails-assets.org' do gem 'rails-assets-alt' gem 'rails-assets-react-router' gem 'rails-assets-moment' end R A I L S - A S S E T S
  187. MISTAKE! F I G H T I N G T

    H E A S S E T P I P E L I N E
  188. TODAY R E A C T- A S S E

    T S
  189. L A N G U A G E

  190. var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string }, render()

    { return ( <div> <div>Name: {this.props.name}</div> </div> ); } }); J S X
  191. var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string }, render()

    { return React.createElement("div", null, React.createElement("div", null, "Name: " + {this.props.name} ); ); } }); J S X
  192. var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string }, render()

    { return ( <div> <div>Name: {this.props.name}</div> </div> ); } }); J S X
  193. var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string }, render()

    { return ( <div> <div>Name: {this.props.name}</div> </div> ); } }); J AVA S C R I P T
  194. @Greeting = React.createClass propTypes: name: React.PropTypes.string render: -> ` <div>

    <div>Name: {this.props.name}</div> </div> ` C O F F E E S C R I P T
  195. @Greeting = React.createClass propTypes: name: React.PropTypes.string render: -> ` <div>

    <div>Name: {this.props.name}</div> </div> ` C O F F E E S C R I P T
  196. @Greeting = React.createClass propTypes: name: React.PropTypes.string render: -> ` <div>

    <div>Name: {this.props.name}</div> </div> ` C O F F E E S C R I P T
  197. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { render() { return ( <div> <div>{this.props.comment}</div> </div> ); } } Greeting.propTypes = { comment: React.PropTypes.string };
  198. E S 2 0 1 5 class Greeting extends React.Component

    { render() { return ( <div> <div>{this.props.comment}</div> </div> ); } } Greeting.propTypes = { comment: React.PropTypes.string.isRequired };
  199. E S 2 0 1 5 class Greeting extends React.Component

    { render() { return ( <div> <div>{this.props.comment}</div> </div> ); } } Greeting.propTypes = { comment: React.PropTypes.string.isRequired };
  200. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { render() { return ( <div> <div>{this.props.comment}</div> </div> ); } } Greeting.propTypes = { comment: React.PropTypes.string.isRequired };
  201. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { getInitialState() { return {foo: "bar"} } render() { return ( <div> <div>{this.props.comment}</div> </div> ); } }
  202. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { getInitialState() { return {foo: "bar"} } render() { return ( <div> <div>{this.props.comment}</div> </div> ); } }
  203. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { getInitialState() { return {foo: "bar"} } render() { return ( <div> <div>{this.props.comment}</div> </div> ); } }
  204. E S 6 / E S 2 0 1 5

    class Greeting extends React.Component { constructor() { thas.state = {foo: "bar"} } render() { return ( <div> <div>{this.props.comment}</div> </div> ); } }
  205. MISTAKE! E A R LY O N W E B

    E T A G A I N S T J S X
  206. TODAY U S E E S 6 / E S

    2 0 1 5 + J S X
  207. O N LY A S M U C H J

    AVA S C R I P T A S Y O U N E E D
  208. None
  209. None
  210. None
  211. None
  212. None
  213. None
  214. @ C H A N TA S T I C

    H I T M E W I T H Q U E S T I O N S
  215. Q U E S T I O N S

  216. D O Y O U U S E F L

    U X ? I F S O , W H I C H L I B R A RY ?
  217. We do use flux in some of our apps. Those

    apps use the library Alt. It is incredibly documented, which is great for teams like ours. It’s available an Bower/rails-assets. We try very hard to use the container pattern with for our components. This makes it very easy to use shored components in isolation or inside of a framework like Alt.
  218. D O E S R E A C T- R

    A I L S A L S O A L L O W Y O U T O U S E J S M O D U L E S ?
  219. No. To use modules, you’ll need to use an integration

    like browserify-rails. Practically, browserify-rails dramatically slowed down our apps in development. We weren’t using the interoperability of js modules enough to justify the loss in development speed. We’re basically waitng for something better to come along.
  220. D O E S R E A C T H

    AV E S O M E T Y P E O F C O R R E S P O N D I N G T E S T F R A M E W O R K ?
  221. Jest, which is also written by Facebook. Jest is a

    pretty thin layer around Jasmine, which is great if you know Jasmine. The React library ships with testing utilities, which allows you to work with any testing tool you have comfort with.