Слайд 1Ember.setState(this.get(React))
Alex Matchneer - 8/28 - Ember NYC
Слайд 2Who’s this guy
Alex Matchneer
@machty
Ember.js Core Team
ExpressCheckoutApp.com
Слайд 3You may remember me from ng-embereño
“This is the only intelligent
comparison I have seen of Ember & Angular”
Troy McClure
Hacker News
User
Слайд 6About React
Open-sourced by Facebook mid-2013
Currently at version 0.11.1
Drives many widgets
in Facebook and much (all?) of the Instagram web experience
GitHub’s
Atom text editor uses it
Definitely some outspoken Ember->React converts
Слайд 7So… what is React?
Component-centric view layer
...that is highly performant
…that encourages
uni-directional data flow and facilitates many lovely functional patterns
Слайд 8Aren’t there like a million JavaScript view layers out there?
So
what?
Слайд 9But it’s important to understand the philosophy behind many of
React’s design decisions.
Yes.
Слайд 10Managing state is treacherous
React Postulate 1
Слайд 11State management is difficult
e.g. which radio button is selected, which
user is logged in, which article are we looking at,
whether a widget is currently being dragged
Слайд 12Two-way bindings are evil
React Postulate 2
Слайд 13Two Way Bindings are Evil
Transparency: effects of a code change
are limited/local, or at least easy to reason about
Also a
useful concept regarding state change
Two-way bindings open Pandora’s box:
No easy way of knowing how far-reaching a change in the reverse direction is going to be
Слайд 20Two-way bindings are like Ice Nine.
Read some Vonnegut if this
makes no sense.
In other words...
Слайд 21Data changing over time is the ultimate root of all
evil
React Postulate 3
Слайд 22So how is React designed to deal with all of
this awfulness?
TL;DR: Everything is Difficult
Слайд 23Uni-directional Data Flow: State vs Props
Components can either be passed
data (props), or materialize their own state and manage it
over time (state)
Passed-in props are immutable (no two-way binding, remember?)
Components explicitly modify their state via this.setState
Слайд 24...data needs to come back upward somehow, right?
But wait...
Слайд 25what if a parent renders a child form? How does
it know when that form was submitted?
Like,
Слайд 26Callbacks
In order for a child to notify its parent of
a state change, a parent must pass the child a
callback as one of its properties
(I’ll talk about alternatives to this later)
Слайд 27Yeah, naw, they’re just callbacks. They’re not events that keep
bubbling or anything like that.
Kinda like actions in Ember, right?
Слайд 28So what about state changes over time being the root
of all evil?
Cool
Слайд 31...literally everything re-renders from that point downward
In React, when you
call setState...
Слайд 34We’re talking giant arrays of stuff
OMG that sounds insane
Слайд 35Doesn’t that just kill performance?
No.
Слайд 37Doesn’t that mean needlessly tearing down and rebuilding DOM?
No.
Слайд 40Virtual DOM
JavaScript representation of DOM nodes, totally separate from the
browser’s slow JavaScript/C++ DOM API
Makes server-side rendering possible (because
Node doesn’t need to have a real DOM API)
And most importantly...
Слайд 42Every time you call setState...
A new virtual DOM tree is
generated
New tree is diffed against old…
...producing a minimum set of
changes to be performed on real DOM to bring it up to date
Слайд 43Hence
No unnecessary/expensive teardown of DOM
Слайд 44Hence
Scrolling doesn’t break (any more than it might with fine-tuned,
granular DOM manipulation)
Слайд 45But you still rebuild / recalculate large objects / arrays?
Turns
out JavaScript is fast, and that that part is rarely
the bottleneck
Слайд 46What about diffing trees?
Isn’t that O(n^3)?
No, due to heuristics based
on how you’ve built up your tree of components.
Слайд 49A preprocessor: XMLish syntax -> JavaScript
Completely optional
Intended to make more
sense to designers (and anyone more comfortable working with HTML,
though it is not exactly HTML)
JSX is
Слайд 51It’s all JavaScript
We don’t need no templates
Слайд 52Templates often live in a separate file (e.g. .hbs)
Contain static
and dynamic portions, e.g. “some text” vs “some {{value}}”
Dynamic portions
“filled in” by whatever template context you pass in
It’s up to JavaScript portion of app to build up that context and expose it to the template
Templates
Слайд 53Handlebars is our templating engine
Controllers or Components are template contexts
that fill in the dynamic bits
Helpers aside, it’s pretty difficult
/ often private API to do lots of DOM rendering in JavaScript
Templates/rendering in Ember
Слайд 54There’s no “template context”
The “context” (the data you have access
to while rendering DOM) is literally everything available in the
render function, and whatever it closes over
...it’s just JavaScript after all
Rendering in React
Слайд 58React / JSX niceties
Minimal syntactic sugar over JavaScript, as opposed
to new syntax/language
No need to re-implement features that already exist
in JavaScript in your templating language of choice
e.g. ES6 / require imports
No template context abstraction
Слайд 59React / JSX drawbacks
Flow control (if blocks, loops), or anything
involving lambdas, are awkward
Often they need to be kept visually
separate from the rest of your render code
Слайд 62React / JSX drawbacks
Ember + Handlebars:
Because Handlebars isn’t “just JavaScript”,
its syntax can stay presentation-focused; e.g. you don’t have to
distinguish between lambdas and values
lambda
Слайд 63So, DOM in JavaScript…?
Seems like poor separation of concerns?
Слайд 64React authors argue the contrary:
Given the extremely tight coupling between
the template and it’s context (a controller/component), the concerns are
the same, and splitting the DOM into a template is an arbitrary separation of technologies rather than a legit separation of concerns.
Слайд 65Hence
In React, everything is a component.
Слайд 66OK, we get it.
Time to pop() the stack, but I
feel it’s worth mentioning:
Слайд 67If you’re going to hate on React for some reason,
make it something other than JSX
Слайд 68stack.pop()
[react, diffing heuristics, JSX]
Слайд 69stack.pop()
[react, diffing heuristics]
Слайд 70We were talking about virtual DOM diffing constraints and heuristics
There
are some rules to abide by, now that we understand
JSX.
Слайд 71Constraint 1: single element
Component.render must return a single element (with
any number of children)
Tempting to want to return something like
, but that’s like returning div(),div() (and React doesn’t support returning arrays)
Слайд 72“Constraint” 2: dynamic portions will be auto-wrapped
“Hello, {name}!”
Hello, Alex!
(fyi: ember
script tags are gone in 1.8 beta!)
Слайд 73Constraint 3: provide keys to large lists
Side note: this error
rules
Слайд 74Constraint 3: provide keys to large lists: why?
You have a
list of players, sorted by name and you insert a
player into that list. Commence DOM diff
Слайд 75Constraint 3: provide keys to large lists: why?
You have a
list of players, sorted by name and you insert a
player into that list. Commence DOM diff
Слайд 76Constraint 3: provide keys to large lists: why?
Lesson: you need
to provide React with key=”” information when rendering lists, or
else it won’t have enough information to efficiently update when you swap out arrays
Слайд 78That’s about it for specifics
At this point you should hopefully
decently grok the basics of React architecture
Слайд 79So what do I think?
I thought you’d never ask
Слайд 80machty’s thoughtz
React is pretty damn cool
The “Re-render everything” programming model
is a major achievement (thanks for proving it was possible,
Facebook R&D)
Quite powerful relative to its simplicity
Слайд 81machty’s thoughtz
React Components are incredibly flexible; because they take you
so much closer to the DOM, there are many widgets
that are just straight up easier to write in React than Ember
Слайд 82If you love it so much why don’t you
steal all
of its good ideas?
Слайд 83We’ll probably steal some ideas
but some of these good React
ideas have some major caveats. Let us explore.
Слайд 84So, why does Ember
provide 2-way bindings if they’re so dangerous?
Слайд 85How evil are 2-way bindings, really?
and how crucial are they
to the Ember programming model?
Слайд 86Two-way bindings can be evil, but...
They have their use cases
What
is a one-way binding other than a two-way binding that
you only use in one direction?
Ember supplies them, but the prevailing (route-driven) patterns encourage / facilitate uni-directional data flow
Слайд 87What are these so called use cases?
Слайд 88Input fields
It’s kinda really nice to just say
{{input value=foo}}
and not have to deal with change events, manually keeping
things in sync
React recognizes this, provides helpers that feel like two-way bindings
React example
Слайд 89What about state change over time?
What does Ember do to
help tame that beast?
Слайд 90First off, let’s just acknowledge that
we have clearly tamed that
beast
Слайд 91Ember dominates the ecosystem of large apps
Whatever we’re doing, it
seems to be working.
Слайд 92But specifically: Ember already has uni-directional data flow
and our bindings
are live, so when upstream data changes, downstream changes too
Слайд 93But Ember has no unified setState
Key-value observation + set()ters fill
the role of setState
setState is nice because it’s easily grep-able;
you can quickly find all the points in your app where data changes
But for Ember->React converts, I don’t think this is actually a major sticking point
Слайд 94Unifying theme: there’s less typing in Ember
But many folk really
like the explicitness of React.
Слайд 96“Yes, you dummy, obviously it’s scalable if Facebook uses it.”
but
actually, it doesn’t take very long before you need to
expand beyond the patterns built into React
Слайд 97Implications of Uni-directional flow
renderComponent(, rootElement)
(... miscellaneous top-level state …)
/>
“5 items in cart
item 1
item 2…”
“5 items in cart”
Слайд 98Implications of Uni-directional flow
renderComponent(, rootElement)
(... miscellaneous top-level state …)
/>
“5 items in cart
item 1
item 2…”
“5 items in cart”
Shared
Cart Model
Слайд 99Need a shared cart model for separate CartDetailsPage and NavCartOverview
Both
components need to update when updates are made to cart
Implications
of Uni-directional flow
Слайд 100This is a common problem in React
Specifically, if you want
to share state between separate components, you have to 1)
find the nearest common ancestor component 2) store the shared state on it, and 3) feed callbacks through all intermediate components to the two components (e.g cart item removal)
Implications of Uni-directional flow
Слайд 101This isn’t very scalable:
Your parent components will continue to
bloat (fat interface problem)
Intermediate components now have added responsibility of
forwarding through callbacks they don’t really care about
Implications of Uni-directional flow
Слайд 102Don’t get me wrong: this pops up in Ember sometimes
too
But in a majority of cases, this is solved by
KVO and model objects that live on a global store
Слайд 103“Global store? Seems pretty easy to throw one of those
into React”
Yes, but it means leaving the happy React land
of uni-directional data flow
Слайд 104Also
it isn’t enough to have access to a shared model
objects; you’ll also need to manually subscribe to change events
for that model, etc.
Слайд 106“More of a pattern than a framework”
Many variants in the
community (reflux, flocks, etc), but all involve some variation of
a pub-sub model of initiating / listening for data changes
Facebook Flux
Слайд 107Not super important to understand a lot of the details
(they also vary)
The key point is that you very quickly
get to a point that you ditch the unidirectional data flow at the React component level and start stashing state in a sideways, global manner
Facebook Flux
Слайд 108Implications of Uni-directional flow
renderComponent(, rootElement)
(... miscellaneous top-level state …)
/>
“5 items in cart
item 1
item 2…”
“5 items in cart”
Some
kind of global store; sideways flow rather than flowing down from top component
Слайд 109We know that strict uni-directionality is impossible for scalable apps
Hence,
we put a lot of love into our APIs for
making this manageable (Ember Data, the container API, dependency injection)
Alright Fancy Pants, but what about Ember?
Слайд 110Alright, wrap it up
I have to mention some cool projects
Слайд 111github.com/rackt/react-router
Ryan Florence’s and Michael Jackson’s brainchild
Heavily inspired by Ember router
Still
some churn, corner cases, gotchas, but extremely well done, and
feels very at home in React yet familiar to Ember devs
React Router (formally react-nested-router)
Слайд 112github.com/swannodette/om
Write apps in ClojureScript, render with react
React’s efficient re-render everything
model is very friendly to immutability programming patterns
Om
Слайд 113github.com/facebook/immutable-js
Immutability patterns, but in JavaScript rather than ClojureScript (Om)
Facebook Immutable
Library
Слайд 114React is awesome
Try it out
Don’t be surprised if Ember adopts
DOM-diffing
Final Thoughts