React vs. Ember - Alex Matchneer - EmberNYC meetup.pptx
- Количество слайдов: 115
Ember. set. State(this. get(React)) Alex Matchneer - 8/28 - Ember NYC
Who’s this guy Alex Matchneer @machty Ember. js Core Team Express. Checkout. App. com
You may remember me from ng-embereño “This is the only intelligent comparison I have seen of Ember & Angular” Troy Mc. Clure Hacker News User
React. js The V in MVC… kinda
About 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 Git. Hub’s Atom text editor uses it Definitely some outspoken Ember->React converts
So… what is React? Component-centric view layer. . . that is highly performant …that encourages uni-directional data flow and facilitates many lovely functional patterns
So what? Aren’t there like a million Java. Script view layers out there?
Yes. But it’s important to understand the philosophy behind many of React’s design decisions.
React Postulate 1 Managing state is treacherous
State 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
React Postulate 2 Two-way bindings are evil
Two 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
Cue poor man’s XKCD
In other words. . . Two-way bindings are like Ice Nine. Read some Vonnegut if this makes no sense.
React Postulate 3 Data changing over time is the ultimate root of all evil
TL; DR: Everything is Difficult So how is React designed to deal with all of this awfulness?
Uni-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. set. State
But wait. . . data needs to come back upward somehow, right?
Like, what if a parent renders a child form? How does it know when that form was submitted?
Callbacks 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)
Kinda like actions in Ember, right? Yeah, naw, they’re just callbacks. They’re not events that keep bubbling or anything like that.
Cool So what about state changes over time being the root of all evil?
In React, when you call set. State. . . literally everything re-renders from that point downward
We’re talking giant arrays of stuff OMG that sounds insane
Doesn’t that just kill performance? No.
Doesn’t mess up scrolling? No.
Doesn’t that mean needlessly tearing down and rebuilding DOM? No.
WTF?
VIRTUAL DOM
Virtual DOM Java. Script representation of DOM nodes, totally separate from the browser’s slow Java. Script/C++ DOM API Makes server-side rendering possible (because Node doesn’t need to have a real DOM API) And most importantly. . .
VIRTUAL DOM DIFFING
Every time you call set. State. . . 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
Hence No unnecessary/expensive teardown of DOM
Hence Scrolling doesn’t break (any more than it might with fine-tuned, granular DOM manipulation)
But you still rebuild / recalculate large objects / arrays? Turns out Java. Script is fast, and that part is rarely the bottleneck
What about diffing trees? Isn’t that O(n^3)? No, due to heuristics based on how you’ve built up your tree of components.
But first. . .
<Wat></Wat> We have to talk about JSX
JSX is A preprocessor: XMLish syntax -> Java. Script Completely optional Intended to make more sense to designers (and anyone more comfortable working with HTML, though it is not exactly HTML)
JSX Examples JSX Java. Script
It’s all Java. Script We don’t need no templates
Templates 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 Java. Script portion of app to build up that context and expose it to the template
Templates/rendering in Ember Handlebars 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 Java. Script
Rendering in React There’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 Java. Script after all
React Example
React Example
React Example
React / JSX niceties Minimal syntactic sugar over Java. Script, as opposed to new syntax/language No need to re-implement features that already exist in Java. Script in your templating language of choice e. g. ES 6 / require imports No template context abstraction
React / 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
React / JSX drawbacks: example
React / JSX drawbacks: example
React / JSX drawbacks Ember + Handlebars: lambda Because Handlebars isn’t “just Java. Script”, its syntax can stay presentation-focused; e. g. you don’t have to distinguish between lambdas and values
So, DOM in Java. Script…? Seems like poor separation of concerns?
React 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.
Hence In React, everything is a component.
OK, we get it. Time to pop() the stack, but I feel it’s worth mentioning:
If you’re going to hate on React for some reason, make it something other than JSX
stack. pop() [react, diffing heuristics, JSX]
stack. pop() [react, diffing heuristics]
We were talking about virtual DOM diffing constraints and heuristics There are some rules to abide by, now that we understand JSX.
Constraint 1: single element Component. render must return a single element (with any number of children) Tempting to want to return something like <div></div>, but that’s like returning div(), div() (and React doesn’t support returning arrays)
“Constraint” 2: dynamic portions will be auto-wrapped “Hello, {name}!” <span>Hello, </span><span>Alex</span><span>!</span> (fyi: ember script tags are gone in 1. 8 beta!)
Constraint 3: provide keys to large lists Side note: this error rules
Constraint 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
Constraint 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
Constraint 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
Constraint 4: keys are required in other use cases Even when lists aren’t involved, you need to provide keys when swapping out similar components whose lifecycle hooks/setup you depend on e. g. swapping out <input> fields; if you’re not careful React might not populate a swapped input field with a default value
That’s about it for specifics At this point you should hopefully decently grok the basics of React architecture
So what do I think? I thought you’d never ask
machty’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
machty’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
If you love it so much why don’t you steal all of its good ideas?
We’ll probably steal some ideas but some of these good React ideas have some major caveats. Let us explore.
So, why does Ember provide 2 -way bindings if they’re so dangerous?
How evil are 2 -way bindings, really? and how crucial are they to the Ember programming model?
Two-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 (routedriven) patterns encourage / facilitate unidirectional data flow
What are these so called use cases?
Input 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
What about state change over time? What does Ember do to help tame that beast?
First off, let’s just acknowledge that we have clearly tamed that beast
Ember dominates the ecosystem of large apps Whatever we’re doing, it seems to be working.
But specifically: Ember already has uni-directional data flow and our bindings are live, so when upstream data changes, downstream changes too
But Ember has no unified set. State Key-value observation + set()ters fill the role of set. State 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
Unifying theme: there’s less typing in Ember But many folk really like the explicitness of React.
“Is React scalable? ”
“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
Implications of Uni-directional flow render. Component(<App/>, root. Element) (. . . miscellaneous top-level state …) <Cart. Details. Page /> “ 5 items in cart <Nav. Bar /> <Nav. Cart. Overview /> item 1 item 2…” “ 5 items in cart”
Implications of Uni-directional flow render. Component(<App/>, root. Element) (. . . miscellaneous top-level state …) <Cart. Details. Page /> “ 5 items in cart <Nav. Bar /> <Nav. Cart. Overview /> item 1 “ 5 items in cart” item 2…” Shared Cart Model
Implications of Uni-directional flow Need a shared cart model for separate Cart. Details. Page and Nav. Cart. Overview Both components need to update when updates are made to cart
Implications of Uni-directional flow This 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 This 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
Don’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
“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
Also 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.
Enter Flux React’s escape hatch
Facebook Flux “More of a pattern than a framework” Many variants in the community (reflux, flocks, etc), but all involve some variation of a pubsub model of initiating / listening for data changes
Facebook Flux Not 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
Implications of Uni-directional flow render. Component(<App/>, root. Element) (. . . miscellaneous top-level state …) <Cart. Details. Page /> “ 5 items in cart <Nav. Bar /> <Nav. Cart. Overview /> item 1 item 2…” “ 5 items in cart” Some kind of global store; sideways flow rather than flowing down from top component
Alright Fancy Pants, but what about Ember? We 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, wrap it up I have to mention some cool projects
React Router (formally react-nested-router) github. 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
Om github. com/swannodette/om Write apps in Clojure. Script, render with react React’s efficient re-render everything model is very friendly to immutability programming patterns
Facebook Immutable Library github. com/facebook/immutable-js Immutability patterns, but in Java. Script rather than Clojure. Script (Om)
Final Thoughts React is awesome Try it out Don’t be surprised if Ember adopts DOM-diffing
Thank you! @machty
React vs. Ember - Alex Matchneer - EmberNYC meetup.pptx