From Back to Front: Rails To React Family

100
From Back to Front: Rails to React Family Khor Soon Hin, @neth_6

Transcript of From Back to Front: Rails To React Family

Page 1: From Back to Front: Rails To React Family

From Back to Front:Rails to React Family

Khor Soon Hin, @neth_6

Page 2: From Back to Front: Rails To React Family

Outline● React Family: ReactJS/Flux/RelayJS/GraphQL● What makes them interesting?● What are they? How they work together?● How do I get started easily?● Should I use them all?● IMPORTANT: How to get them onto Rails

Page 3: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: In A Sentence Each

● React: UI● Flux: Data Flow● GraphQL: Data Retrieval● Relay: Component-Data Co-location

Page 4: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Goal

Improve software quality given the same development time

Page 5: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Goal

Improve software quality given the same development time

● Easy to understand & debug● Less prone to error

Page 6: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Approach

Simplify abstraction to push common tasks/code organization to pattern/framework

Page 7: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Approach

Simplify abstraction to push common tasks/code organization to pattern/framework

● Easy to understand & debug

Page 8: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Approach

Simplify abstraction to push common tasks/code organization to pattern/framework

● Easy to understand & debug● Less prone to error

Page 9: From Back to Front: Rails To React Family

React/Flux/Relay/GraphQL: Common Outcome

Functional/Declarative Programming

Page 10: From Back to Front: Rails To React Family

React: UI

Page 11: From Back to Front: Rails To React Family

React

● Single-Page Application (SPA)

Courtesy: https://facebook.github.io/react/docs/thinking-in-react.html

Page 12: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Page 13: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Page 14: From Back to Front: Rails To React Family

React (cont.)Abstraction

Each React element knows:

● The data it needs● How to render itself with HTML fragments● The data it passes to its children

Page 15: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Fetch Data

Page 16: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Page 17: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Page 18: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Uni-directional Flow

Page 19: From Back to Front: Rails To React Family

React (cont.)

● Single-Page Application (SPA)● Cascading Views

Debug Upwards

Page 20: From Back to Front: Rails To React Family

React (cont.)

jQuery: You need to find all the pieces of code that modified the UI in error

Page 21: From Back to Front: Rails To React Family

React: User Interaction

ball

Page 22: From Back to Front: Rails To React Family

React: User Interaction (cont.)

ball

Page 23: From Back to Front: Rails To React Family

React: Data Change (cont.)

ball

Page 24: From Back to Front: Rails To React Family

React: User Interaction (cont.)

ball

Page 25: From Back to Front: Rails To React Family

React: User Interaction (cont.)

jQuery: You need to find element to modify and make modification

Page 26: From Back to Front: Rails To React Family

Imperative vs. Functional: Coding● Imperative:

○ Example: jQuery code■ Code is likely in a monolithic Javascript■ Hold element ids for where each piece of data should be shown■ Retrieves element and display data■ Complex model requires customized code:

● Find pieces of data, and update pieces of element

● Functional:○ Example: React code

■ Code is contained within each React element:■ Each React element declares which part of data to pass on to children■ Each React element declares how to render data it receives■ Simplified model reduces code relinquish more to framework:

● Provide data to top view, and cascading views redraws themselves

Page 27: From Back to Front: Rails To React Family

Functional vs. Imperative: Benefits● Imperative:

○ Complex model requires customized code■ Dig through customized code to understand relationships■ Different pieces of code to initialize UI, and update based on interaction

● Functional:○ Simplified model reduces code relinquish more to framework

■ Relationship and data-flow is top-down■ Same piece of code to initialize, and update UI

Page 28: From Back to Front: Rails To React Family

React On Rails● https://github.com/reactjs/react-rails● app/assets/javascripts/components/xxxxx.js

○ Dump all your React components related to xxxxx

Page 29: From Back to Front: Rails To React Family

React: Codevar DATA = [ { category: 'Sporting Goods', products: [ {price: '$49.99', stocked: true, name: 'Football'}, {price: '$9.99', stocked: true, name: 'Baseball'}, {price: '$29.99', stocked: false, name: 'Basketball'}, ], }, { category: 'Electronics', products: [ {price: '$99.99', stocked: true, name: 'iPod Touch'}, {price: '$399.99', stocked: false, name: 'iPhone 5'}, {price: '$199.99', stocked: true, name: 'Nexus 7'}, ], },];

Page 30: From Back to Front: Rails To React Family

React: Code (cont.)// Javascript required in app/assets/javascripts/application.js

ReactDOM.render( <ProductTable data={DATA} />, document.getElementById('container'));

Page 31: From Back to Front: Rails To React Family

React: Code (cont.)

// app/assets/javascripts/components/product.jsx

var ProductTable = React.createClass( render: function() { var rows = []; this.props.data.forEach(function(dataElem) { rows.push(<ProductCategoryRow category={dataElem.category} … />); dataElem.products.forEach(function(product) { rows.push(<ProductRow product={product} … />); }); ...

var DATA = [ { category: 'Sporting Goods', products: [ {name: 'Football', ...}, {name: ’Baseball’, ... }, ... ], }, { category: 'Electronics', products: [ {name: 'iPod Touch', ...}, {name: ‘iPhone 6’, …}, ... ], },];

Page 32: From Back to Front: Rails To React Family

React: Code (cont.)

// app/assets/javascripts/components/product.jsx

var ProductCategoryRow = React.createClass({ render: function() { return (<tr><th>{this.props.category}</th></tr>); }});

Page 33: From Back to Front: Rails To React Family

React: Code (cont.)// app/assets/javascripts/components/product.jsx

var ProductRow = React.createClass({ render: function() {

return ( <tr> <td>{this.props.product.name}</td> <td>{this.props.product.price}</td> </tr> );

}});

Page 34: From Back to Front: Rails To React Family

React: SummarySimplify abstraction

● UI○ Shove data for entire UI from the top component○ Each component render itself with data for self○ Each component pass data to child○ To change component, just change data

Framework

● Handle initializing/updating UI with the right pieces of data

How

● Re-drawing all UI pieces efficiently

Page 35: From Back to Front: Rails To React Family

Flux: Data Flow

Page 36: From Back to Front: Rails To React Family

React

Courtesy: https://facebook.github.io/react/docs/thinking-in-react.html

Page 37: From Back to Front: Rails To React Family

MVC

Courtesy: http://fluxxor.com/what-is-flux.html

Page 38: From Back to Front: Rails To React Family

Multiple Models/Domains

Chat

News

Summary

Friend Requests

Page 39: From Back to Front: Rails To React Family

MVC ComplexityExplosion of flows

Courtesy: http://fluxxor.com/what-is-flux.html

Page 40: From Back to Front: Rails To React Family

MVC Complexity

Courtesy: http://fluxxor.com/what-is-flux.html

News

SummaryNews

One model powering 2 views

Page 41: From Back to Front: Rails To React Family

MVC Complexity

Courtesy: http://fluxxor.com/what-is-flux.html

News

SummaryNews

Friends

One model powering 2 views

Two models powering 1 view

Page 42: From Back to Front: Rails To React Family

MVC ComplexityExplosion of flows

Error in UI

Page 43: From Back to Front: Rails To React Family

MVC Complexity (cont.)Explosion of flows

Error in UI

Page 44: From Back to Front: Rails To React Family

MVC Complexity (cont.)Explosion of flows

Error in UI

Page 45: From Back to Front: Rails To React Family

MVC Complexity (cont.)Explosion of flows

Error in UI

Page 46: From Back to Front: Rails To React Family

Tracing LoopsDifficult:

● Tons of log entries○ Lots of things to look through○ Scrolling is laggy

● Cannot just look at snapshot for messy loops

Page 47: From Back to Front: Rails To React Family

MVC Simple Loops

Error in UI

Tracing (if set correctly) may tell you where loop is

Page 48: From Back to Front: Rails To React Family

MVC Looping the LoopExplosion of flows

Error in UI

Depending on where you look in the trace, you may think orange or bold loop

A loop resulting in another loop

Page 49: From Back to Front: Rails To React Family

MVC Simultaneous LoopsExplosion of flows

Error in UI

Tracing is difficult; seeing two traces

A loop resulting in multiple other loops concurrently

Page 50: From Back to Front: Rails To React Family

Flux

Courtesy: http://fluxxor.com/what-is-flux.html

Trigger ActionAction Done

Action DoneState Update

Structured and self-contained

Page 51: From Back to Front: Rails To React Family

Flux (cont.)

Courtesy: http://fluxxor.com/what-is-flux.html

Take ActionAction Done

Action DoneState Update

● Throttles one Action at a time● waitsFor()

● Synchronous tasks only● State and logic co-located

Page 52: From Back to Front: Rails To React Family

Flux (cont.)

Courtesy: http://fluxxor.com/what-is-flux.html

register register

Page 53: From Back to Front: Rails To React Family

With Flux

Courtesy: http://fluxxor.com/what-is-flux.html

Page 54: From Back to Front: Rails To React Family

Side-by-side

MVC Flux

Page 55: From Back to Front: Rails To React Family

Side-by-side● Structured growth (no path explosion):

○ Parallel stores lead to single top view

MVC Flux

Page 56: From Back to Front: Rails To React Family

Side-by-side● Structured growth (no path explosion):

○ Parallel stores lead to single top view○ Single-parent cascading views

MVC Flux

React

Page 57: From Back to Front: Rails To React Family

Side-by-side● Structured growth (no path explosion):

○ Parallel stores lead to single top view○ Single-parent cascading views

● Controllers-Models collapsed into Stores to co-locate logic & state

MVC Flux

React

Store

Store

Page 58: From Back to Front: Rails To React Family

With Flux

Error in UI

Only one trace at a time

● Dispatcher throttles Actions

A loop resulting in multiple other loops concurrently

Page 59: From Back to Front: Rails To React Family

With Flux

Error in UI

register

register

Page 60: From Back to Front: Rails To React Family

With Flux

Error in UI

register

register

Page 61: From Back to Front: Rails To React Family

Todo App

New Todo

Create

Todo #1

Todo #2Created Todo List

Page 62: From Back to Front: Rails To React Family

Action Creator Trigger<form>

<input id=’todo-text’ type=’text’ />

<button onClick=TodoActions.create($(‘#todo-text’).val())>Create</button>

</form>

Page 63: From Back to Front: Rails To React Family

Action CreatorTodoActions: { create: function(text) { // Take some action, e.g., call REST API AppDispatcher.dispatch({ actionType: TodoConstants.TODO_CREATE, // Basically ‘create’ text: text }); }, ….}

Page 64: From Back to Front: Rails To React Family

DispatcherAppDispatcher = require('flux').Dispatcher;

Page 65: From Back to Front: Rails To React Family

StoreAppDispatcher.register(function(action) { // action is passed in by Action Creatorvar event = action.event; switch(action.actionType) { case TodoConstants.TODO_CREATE: // Do whatever, e.g., update local store data or fetch fresh data from server TodoStore.emitChange(); break; …. }}

register

Page 66: From Back to Front: Rails To React Family

Store (cont.)var TodoStore = assign({}, EventEmitter.prototype, { // EventEmitter provides emit, on, removeListener, etc. methods addChangeListener: function(callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener: function(callback) { this.removeListener(CHANGE_EVENT, callback); }, emitChange: function() { this.emit(CHANGE_EVENT); }, ...}

register

Page 67: From Back to Front: Rails To React Family

Controller-View// This is where React is usedvar TodoApp = React.createClass({ componentDidMount: function() { TodoStore.addChangeListener(this._onChange); }, componentWillUnmount: function() { TodoStore.removeChangeListener(this._onChange); }, _onChange: function() { this.setState(TodoStore.getData()); }, ...}

register

Page 68: From Back to Front: Rails To React Family

Flux: More Pattern than Framework● Many implementations● Flux:

○ By Facebook

● Redux:○ Not pure Flux per se, but embraces its spirit○ Server-side rendering built-in○ Growing super-fast

● Alt:○ Pure Flux compliant○ Well-documented, great community○ Server-side rendering built-in

Page 69: From Back to Front: Rails To React Family

Flux Documentation Quirks● Uses native dispatcher, not the official Facebook dispatcher

○ Use official Facebook dispatcher

● Does not use Flux-Utils○ Use Flux-Utils to create Stores without code duplication

Rails with pure Flux: https://medium.com/@khor/back-to-front-rails-to-facebook-s-flux-ae815f81b16c

Page 70: From Back to Front: Rails To React Family

GraphQL: Data Retrieval

Page 71: From Back to Front: Rails To React Family

GraphQL

I speak GraphQL

API Endpoint

Page 72: From Back to Front: Rails To React Family

GraphQL (cont.)

API Endpoint

query { store(email: "[email protected]") { name, address }}

Page 73: From Back to Front: Rails To React Family

GraphQL (cont.)

API Endpoint

query { store(email: "[email protected]") { name, address }}

store(email: “[email protected]”) { name: ‘Hello Shop’, address: ‘1-3-1 Aoyama’}

Page 74: From Back to Front: Rails To React Family

GraphQL (cont.)

API Endpoint

query { store(email: "[email protected]") { categories { name, products { name, price, stock } } }}

store { categories: [ { name: ‘Sporting Goods’, products: [ { name: ‘Football’, price:, stock: 50 }, … }, ... ]}

From React example

Page 75: From Back to Front: Rails To React Family

GraphQL (cont.)

API Endpoint

query { store(email: "[email protected]") { categories { name, products { name, price, stock } } }}

store { categories: [ { name: ‘Sporting Goods’, products: [ { name: ‘Football’, price:, stock: 50 }, … }, ... ]}

Single endpoint

Nested data query

Client-specified query

Data in 1 round-trip

Page 76: From Back to Front: Rails To React Family

GraphQL (cont.)REST API:

● 3 endpoints:○ Endpoint for Store○ Endpoint for Category○ Endpoint for Product

● Server-controlled query○ Query for store returns email, name, address, etc. whether you want it or not

● Multiple round-trips vs. impure-REST/endpoint proliferation○ Get Store, get each Category, get each Product in each Category○ Lumping them together creates view-specific endpoints

Page 77: From Back to Front: Rails To React Family

GraphQL Enabled

https://github.com/rmosolgo/graphql-ruby

I speak GraphQL

API Endpoint

Page 78: From Back to Front: Rails To React Family

GraphQL: Schema # app/graph/types/query_type.rb

field :store do type StoreType # Shape of this type argument :email, !types.String, "Email of Store owner" ... # Retrieve data resolve -> (obj, args, ctx) do Store.find_by_email(args[:email]) # ActiveRecord stuff endend

query { store(email: "[email protected]") { name, address }}

Page 79: From Back to Front: Rails To React Family

GraphQL: Schema (cont.) # app/graph/types/store_type.rb

StoreType = GraphQL::ObjectType.define do name "Store" description "Store Data" … field :name, !types.String, "Store name" # From ActiveRecord field field :address, !types.String, "Store address" # From ActiveRecord field field :categories, !types[CategoryType], “Category list” # From ActiveRecord fieldend

query { store(email: "[email protected]") { name, address }}

Page 80: From Back to Front: Rails To React Family

GraphQL: Schema (cont.) # app/graph/types/store_type.rb

StoreType = GraphQL::ObjectType.define do name "Store" description "Store Data" … field :name, !types.String, "Store name" # From ActiveRecord field field :address, !types.String, "Store address" # From ActiveRecord field field :categories, !types[CategoryType], “Category list” # From ActiveRecord fieldend

query { store(email: "[email protected]") { categories { name, products { name, price, stock } } }}

Page 81: From Back to Front: Rails To React Family

GraphQL: Schema (cont.) # app/graph/types/category_type.rb

CategoryType = GraphQL::ObjectType.define do name "Category" description "Category Data" … field :name, !types.String, "Category name" # From ActiveRecord field field :products, !types[ProductType], “Product list” # From ActiveRecord field ...end

query { store(email: "[email protected]") { categories { name, products { name, price, stock } } }}

Page 82: From Back to Front: Rails To React Family

GraphQL: SummarySimplify:

● Efficient request for data that you need from server endpoint

Framework

● A single API endpoint to respond to query● Query language that enables client to specify data required, ala SQL● No over-fetch● Request all data in a single query

Page 83: From Back to Front: Rails To React Family

Relay: Component-Data Co-location

Page 84: From Back to Front: Rails To React Family

Relay: Declare Data

Page 85: From Back to Front: Rails To React Family

Relay: Declare Data

Page 86: From Back to Front: Rails To React Family

React: Recap (cont.)Fetch Data (AJAX)

Page 87: From Back to Front: Rails To React Family

React: Recap (cont.)

Page 88: From Back to Front: Rails To React Family

React: Recap (cont.)

Page 89: From Back to Front: Rails To React Family

Relay: GraphQL Fetch Relay/GraphQL

Page 90: From Back to Front: Rails To React Family

Relay: Why Data Declaration? Pass All Data

Page 91: From Back to Front: Rails To React Family

GraphQL/Relay Enabled

gem ‘graphql-ruby’

I speak GraphQL/Relay

API Endpoint

gem ‘graphql-relay-ruby’Relay Layer

‘react-relay’

Page 92: From Back to Front: Rails To React Family

Relay/GraphQL on Rails

Relay/GraphQL on Rails: https://medium.com/@khor/relay-facebook-on-rails-8b4af2057152

Page 93: From Back to Front: Rails To React Family

Relay: SummarySimplify

● Data fetching related stuff○ Show ‘Loading….’○ Data re-fetching○ Caching

■ Figuring out batching, reading/writing caching, fetching only missing pieces■ NOTE: Caching is NOT new but handling caching for hierarchical data is rather new

○ Managing errors, retry failed requests○ Optimistic updates○ Pagination

Framework

● Use GraphQL to fetch all required data declaratively● Handle all the above

Page 94: From Back to Front: Rails To React Family

How To Get Started Easily?● Make one piece of UI React● Make another piece React● Repeat until SPA

Page 95: From Back to Front: Rails To React Family

Which Should I UsePartly from: https://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html

Flux Relay/GraphQL

Multiple stores (one per domain) Single store (GraphQL server)

Explicit subscription Implied subscription based on data required

Actions (possibly with AJAX) Mutations (query with changes)

REST API pros, and cons Efficient data handling (batching, caching, no over-fetching, etc.)

More customized coding Generic framework for data access, updates, etc.

Prefer imperative debugging Love magic, abstractions, and puzzle debugging

Page 96: From Back to Front: Rails To React Family

Other Stuff● React

○ Must all user-interaction go to top? Must top do all data manipulation?

● GraphQL○ Can I just use GraphQL? Do I need Relay?

● Relay○ How to modify Model without AJAX?○ There is no gem?

Page 97: From Back to Front: Rails To React Family

Thank You!

Page 98: From Back to Front: Rails To React Family

Primary ResourcesThinking in React: https://facebook.github.io/react/docs/thinking-in-react.html

Flux Best Practices: https://facebook.github.io/flux/docs/flux-utils.html

Nested Relay Components: https://facebook.github.io/react/blog/2015/03/19/building-the-facebook-news-feed-with-relay.html

Page 99: From Back to Front: Rails To React Family

Practical ResourcesRails with pure Flux: https://medium.com/@khor/back-to-front-rails-to-facebook-s-flux-ae815f81b16c

Relay/GraphQL on Rails: https://medium.com/@khor/relay-facebook-on-rails-8b4af2057152

Relay/GraphQL Rails Starter Kit: https://github.com/nethsix/relay-on-rails

Page 100: From Back to Front: Rails To React Family

ReferencesFlux Comparisons:

● https://medium.com/social-tables-tech/we-compared-13-top-flux-implementations-you-won-t-believe-who-came-out-on-top-1063db32fe73

● http://jamesknelson.com/which-flux-implementation-should-i-use-with-react/

Introduction to GraphQL: http://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html

Thinking In GraphQL: https://facebook.github.io/relay/docs/thinking-in-graphql.html

Thinking In Relay: https://facebook.github.io/relay/docs/thinking-in-relay.html