Removing User Interface Complexity, Or Why React is Awesome

Embed Size (px)

Citation preview

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    1/26

    Removing User Interface Complexity, or WhyReact is AwesomeMay 13, 2014

    I've been studying frameworks and libraries like Ember, Angular, and React

    the past several months, and given Web Components a lot of thought. These

    all solve similar problems to varying degrees, and are in conflict in some ways

    and in harmony in others.

    I'm mostly concerned with the core problems of data binding and

    componentizing UIs. After much research and usage of the technologies

    mentioned above, I found React to provide the best solution.

    I ask that you set aside your framework prejudices and read this post with an

    open mind. This post is not to evangelize React specifically, but to explain wh

    its technique is profound. I want developers steeped in other technologies to

    take a hard look at these techniques, particularly those involved in Web

    Components.

    This post actually started as a call to address problems with Web Components

    But I've come to realize a few things: the bottom half of the Web Components

    effort (unlocking all the builtin stuff so that even native tags could berewritten) is a great thing, Web Components are a step up from current

    technology regardless, and I don't want to do harm to that effort. My concerns

    are quite vague as well, so it doesn't make a good blog post. I can't reconcile

    what I want components to be with Web Components, and it's much better if

    fully explain what I think is the best solution and leave it up to the readers to

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    2/26

    varMyToolbar = require('shared-components/toolbar');

    apply if they choose.

    The Bloop Library

    I'm not going to focus on React specifically. In fact, just in case it would bedistracting, we're not going to use React at all.

    e're going to build our own library from scratch that is highly inspired by

    React. This lets us play with all kinds of ideas with a very small amount of

    code. This is prototype-quality, of course, and wouldn't scale to large UIs, but

    using something like React would make it scale.

    Let's call our library Bloop. Our library should let us change data and

    transparently keep the UI in sync. It should allow us to structure the UI into

    components, and flow data between them sanely.

    First, we need a way to represent behavior and structure. It's easy to write

    behaviors as JavaScript, but what about the structure? We could use a

    templating language that looks like HTML, but it's much easier just to

    represent it in JavaScript too. It keeps the component together, makes it trivial

    to wire up the structure with behaviors, and allows us to easily share the

    component. Just imagine being able to use JavaScript for importing a

    component:

    You can't even do something as basic as that with Web Components.

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    3/26

    varBox = Bloop.createClass({ getInitialState: function(){ return{ number: 0}; },

    updateNumber: function(){ this.state.number++; },

    render: function(){

    returndom.div( this.state.number, dom.button({ onClick: this.updateNumber }, 'Increment') ); }});

    Bloop.renderComponent(Box(), document.body);

    This is what a component looks like with our library. It shows a number and a

    button that, when pressed, increments the number. getInitialStatereturns

    the initial state of the object that can be accessed with this.state.

    Next, we need a way to render it into the page. You can use renderComponent

    to render a component instance into a DOM element.

    There's a problem with this though: it doesn't rerender whenever the state

    changes. TheBox

    component mutates its state directly, so we don't know whesomething has changed. We could add a setmethod that tells us what is

    changing, but then we need to figure out which pieces of the DOM to update,

    which gets really complicated. Here's an idea: rerender the entire app

    whenever a change is made. That way, it's really easy to keep everything in

    sync.

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    4/26

    varbox = Box();

    function render(){ Bloop.renderComponent(box, document.body); requestAnimationFrame(render);}

    render();

    But how do we know when a change is made? A setmethod to change state

    could trigger a rerender, but there's an even easier way to react to changes:

    continuously call renderComponentwith requestAnimationFrameto repaint

    the UI, and only change the DOM when the component returns different

    content. React does not do thisby default; it has a setStatemethod to chang

    state that triggeres a repaint. Let's just have some fun with

    requestAnimationFrame, even though you would never do this in

    production. View the full source code for this example here.

    Rerendering everything (and only applying it to the DOM when something

    actually changed) vastly simplifies the architecture of our app.Observables+DOM elements is a leaky abstraction, and as a user I shouldn't

    need an intimate knowledge of how the UI is kept in sync with my data. This

    architecture opens up lots of various ways to optimize the rendering, but it's

    all completely transparent to the user.

    The Bloop library is only 250 lines of JavaScript, and simply renders a

    component by setting innerHTMLif changed. Since you usually have a top-

    level Appcomponent, that means the entire app is rendered with innerHTML. I

    told you it was prototype-quality, right? In a twisted, absurd way, it's shockin

    how far you can go with this. And the kicker is you can easily swap this out

    with React to get much better rendering performance, since React has a virtua

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    5/26

    DOM and will only touch the real DOM when needed. That also solves variou

    problems like rerendering forms and other controls which have focus.

    Since everything is rerendered on update, we've decoupled data binding and

    views and libraries can reuse it in many different ways like Om. Even though

    React implements complicated optimizations, the mental footprint is as small

    as our Bloop library which is a breathe of fresh air amid other complicated

    data binding technologies.

    Data Flow

    e expressed our component's structure in JavaScript instead of trying to

    mangle it into the DOM. This makes data flow very natural, since we have

    direct access to the component instance. If you use the DOM directly, it's

    common to have to wire up the data flow in JavaScript afterwards. A

    templating engine with automatic bindings helps, but it still creates more

    complexity than necessary. The days of having one big HTML file are gone;

    components and their behaviors are intimately dependant and should be

    encapsulated like so.

    Aren't you tired of having to query the DOM tree and manually manage the

    structure to create UIs? Web Components doesn't solve this at all, it just tries t

    encapsulate the work. The problem is that building apps isbuilding

    components, so you inevitably are forced back into the manual DOMmanagement to create your app-specific components(like how you

    constantly have to create directives in Angular). You also need to jump into

    JavaScript to configure and wire up any Web Components you used. It's a ver

    messy abstraction, and fools you into desiring a pure HTML-based declarative

    way to write apps, which is like wanting steak but eating liver.

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    6/26

    varApp = Bloop.createClass({ getInitialState: function(){

    Frameworks like Ember and Angular help a lot with this. However, templates

    and controllers are still separated and data binding still leaks into your app

    (especially in Angular, $scope.$applyis the devil). Ember does a better job

    than most, but you still have to declare data dependencies manually

    (computed properties) and ultimately accept its way of modelling data. That's

    not a bad thing but there are things you can't do that we will study later in thi

    article. It simply comes down to the framework vs. library debate.

    In Bloop, there's a clear and simple flow of data: data is passed down and

    events flow up. Here is the same example as before, but with multiple

    components to demonstrate data flow. In all of my examples, you can assume

    domis set to Bloop.domand that the Appcomponent is continuously renderedwith requestAnimationFramelike you saw in the first section.

    There are two components: Toolbarwhich makes a few buttons that change

    the number, and Appwhich is our top-level component that uses Toolbar. App

    has state: the current value of the number. It passes this state into Toolbar, so

    that toolbar can decrement and increment the number. ButToolbar

    nevertouches our app state; it can make a new number, and call the onChange

    handler with the new number, but it can't do anything else. It's up to the App

    component to bind the onChangehandler to one of its methods which takes th

    new number and actually modifies the state.

    This introduces another aspect of Bloop: properties. Properties are available as

    this.propsand represent the data passed into the component. Components

    should never mutate their properties. View the full source for this example

    here.

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    7/26

    return{ number: 0}; },

    updateNumber: function(value){ this.state.number = value; },

    render: function(){ returndom.div( dom.span(this.state.number), Toolbar({ number: this.state.number, onChange: this.updateNumber }) ); }});

    varToolbar = Bloop.createClass({ render: function(){ returndom.div( dom.button({ onClick: this.props.onChange.bind(null, this.props.number - 1 }, 'decrement'), dom.button({ onClick: this.props.onChange.bind(null, this.props.number + 1 }, 'increment') ); }});

    State flows down the components, and events flow up. Note that while

    properties should never be changed, state is mutable. Properties can't be

    changed because they are inherited every time the component is rendered, so

    any changes will be lost.

    The difference between state and properties can be useful. It makes it clear

    what state the component owns. It's best to keep most of your components

    stateless, and isolate state into as few places as possible. This makes it easy to

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    8/26

    rationalize about your app and how it changes over time. We will explore this

    more in the next section.

    This example is trivial and so far we've been pretty abstract. You will see more

    complex examples throughout this post. Communication between component

    is difficult to do right, and Bloop and React provide you with this simple data

    flow for handling it, even if it may be limiting in certain cases (you many find

    yourself adding a lot of trivial methods to Appto change app state). Check out

    this and this page from React about it. Near the end of article, we will explore

    modifications to this approach.

    Let's quickly look at a more complex example. This app has tabs that switchbetween content, and a settings pane that lets you customize it. View the code

    here. There is a Tabs component that responds to tab changes by calling the

    onChangehandler, and we bind that to the changeTabmethod on our top-leve

    Appcomponent.

    Additionally, there is a Settingscomponent that renders the settings form.

    henever a setting changes, all it does is call its onChangeproperty, which we

    bound to the changeSettingmethod on App.

    Fundamentally, this is a declarative way to construct components. The render

    method continuously constructs a new UI based on simple JavaScript objects,

    binding specific events to component methods.

    Explicit App State

    You're probably already familiar with the pattern of attaching event handlers

    to components. As described above, Bloop takes this even further though,

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    9/26

    making it clear that events flow upward, and since everything is in JavaScript

    it's dead simple to take a JavaScript function and bind it right onto the

    component. You do this declaratively within renderwhen creating the

    component.

    There's a deeper reason why it's so important to make data flow clear and

    simple: it encourages you to keep state in as few places as possible and make

    most of your components stateless. It's easy to create complex data flows in

    Bloop with many small components, and keep track of what's going on.

    Additionally, instead of setting state directly on a component instance, Bloop

    makes it explicitly a separate JavaScript object. Components that do have state

    can access it with this.state. A component that uses state must implement agetInitialStatemethod that returns the initial state object.

    Tearing apart state from the component instance turns out to be really

    powerful. It fits well with the model that most of our state is held at the top-

    level, since most of your UI is now described in one simple JavaScript object.

    This has far-reaching consequences.

    1. It's adaptable.The state object doesn't have to be a native JavaScript

    object; it can be anything you return in getInitialState(or your own

    way of passing state around, if you choose). Want to use persistent data

    structures instead? Go ahead!

    2. It's easy to snapshot.Given a specific state, you can guarantee what the

    resulting HTML of a component will be. If you simply save the state objecsomewhere, you can load it up later and render your component exactly

    like it was when you saved it. An undo system is trivial: just save the

    state, and restore it (which is especially trivial with persistent data

    structures).

    3. It's easy to test and prerender.Similar to point #2, you can easily test

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    10/26

    components by rendering them with a specific state to an HTML string

    and comparing the output. You can even manually fire off event handlers

    which change state and test the changes. Finally, prerendering on the

    server is as trivial as it sounds: render the top-level component to a string

    and serve it up, and when loaded on the client the library will bind all the

    event handlers to the prerendered DOM.

    The principle to learn is that things like DOM elements are basically native

    objects, like an open file instance. You don't stick user-land state onto file

    instances, do you? They are unserializable and slow. Since the DOM doesn't

    contain our app state, we just have to deal with a simple JavaScript object,

    declaratively render a structure based on it, and let the library figure out howto reify it into DOM elements.

    The kicker is that the opportunity for great performance falls out of this

    naturally. React creates a lightweight Virtual DOM every time components are

    rendered, and diffs them to figure out the smallest amount of real DOM

    changes needed. The result is astonishing performance since DOM changes ar

    the slowest part. Of course, you don't have to do this; Bloop naively sets

    innerHTMLif the contents have changed, but the abstraction is there to allow

    great optimization.

    Ok, enough philosophizing, let's get to some examples.

    Looking at our app with tabs again, there is a Tabscomponent. This is acompletely stateless component, and the top-level Appcomponent actually

    handles the tab change and changes selectedItemin the app state. You migh

    think that Tabsshouldhandle the state to be reusable, but if you think about it

    somethingneeds to be hooked up to change the panes when a tab is changed.

    This makes the dependency on that state explicit and easy to rationalize about

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    11/26

    waiting on app state...

    In fact, all of our app state is a single object attached to theAppcomponent. It'

    easy to expose this as an editor, which is what you see below. This is the raw

    state of the app, and is synced both ways. Change it manually in the textarea

    below (change bigFontsto true, for example), and click around the app to

    watch changes come in.

    hen you change it in the textarea above, it is sent to the app and applied by

    running app.state = JSON.parse(msg.data). It's that easy.

    Generally, your app state will roughly correspond to your UI structure. I thinkthis is what efforts like Web Components are trying to do (hide details about

    the UI structure), but it doesn't really work if you insist on still using the DOM

    This is what you reallywant: a stripped down, bare representation of your app

    Your UI structure falls out it of this, not the other way around.

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    12/26

    varprevStates = [JSON.stringify(appState)];

    function undo(){ while(1) { varstate = JSON.parse(prevStates.pop());

    if(!prevStates.length) { // This is the initial app state, so unconditionally apply it // and push it back onto the history so we don't lose it appState.feed = state.feed; prevStates.push(JSON.stringify(state));

    break; } elseif(JSON.stringify(appState.feed) !== JSON.stringify(state.fe

    // We found a state where the feed has changed, so apply it appState.feed = state.feed; break; } }}

    function render(){ app.state = appState; varchanged = Bloop.renderComponent(app, document.body); if(changed) { prevStates.push(JSON.stringify(appState)); } requestAnimationFrame(render);}

    The app state is so simpleand easy to access. Let's implement an undo system

    and see if we were telling the truth about how easy it is. Let's use a different

    example app for this: a basic twitter clone (code here).

    Type some messages into the text input, and press "enter" to submit them.

    Click the star to "star" a few of them. Now press the "undo" button several

    times and you will watch your previous actions wash away.

    How is this implemented? Here is the code:

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    13/26

    render();

    All we have to do is save the app state when it is changed, and apply a

    previous state when an undo is requested. There are a few architecture-specifi

    details here: Bloopdoesn't currently have an event when the state changes, buBloop.renderComponentdoes return if the rendered output has changed, so

    we use that to detect when we should save the state. And since we are using

    simple JavaScript objects, we use JSON.parseand JSON.stringifyto take

    snapshots of the state. This is very simplistic, but you could implement more

    powerful ways to track changes like using persistent data structures.

    Note that we only undo changes to the feed. In undo, we walk back through

    the history and find an app state where the feedstructure has changed,

    skipping over any other state changes. It's up to you to determine what you

    want to track and undo.

    If you're undoing UI that is backed by a data store, you also need to perform

    the undo in the backend. You can use a versioned data store, which makes it

    just as trivial. Or you can diff the app state and generate actions to perform th

    undo. This is something that needs to be explored more, and is the reverse of

    usual undo methods, where you manually undo changes in the database and

    then re-fetch data and refresh the whole UI. That might be just fine for your

    app, but it gets tedious to hand-code undo code paths for every single model.

    This is far more powerful because it's automatically applicable to anycomponent.

    Forcing your data through the backend data store to allow undo is also

    limiting. What if you want to not actually persist the action for 30 seconds, and

    give the user the chance to undo before it even hits the backend? Our

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    14/26

    Bloop.renderComponentToString(Toolbar({ username: 'foo'}))

    // Output:// "Logged in as foosettings

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    15/26

    Bloop.createClass({ render: function(){ varitems = this.props.items.filter(function(item){ returnitem.isVisible; });

    returnitems.map(function(item){ returndom.div(item.name); }); }})

    Bloop itself is extremely simplistic, but we already get most of this for free.

    React makes it easy to use these techniques for real apps because it handles all

    the hard stuff, too. There's a lot of win here, and a lot of opportunity for

    interesting advancements.

    Game Loop

    So far, we've said that the structure of a component created within the render

    method is declarative. This is because you generate the structure based off of

    the app state, nothing else. As the app state changes, so does your structure.

    However, it may not look like traditionally declarative code, since anyJavaScript can be run:

    There is a declarative current running underneath this code. But we can look a

    it in a different light: almost as if we're operating in an immediate rendering

    mode, as if the dom.divand such functions painted the element instantly. In

    fact, since we're using requestAnimationFrameto repaint the UI, this is

    extremely similar to how game developers render UI in games.

    Game developers discovered immediate-mode graphical user interfaces years

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    16/26

    ago (watch that video, it's awesome). Their power lies in the fact that you just

    run normal JavaScript to paint your UI: conditional elements are literally

    expressed as if(shown) { renderItem(); }, and that data is always synced

    with the UI because the UI is always redrawn.

    The web traditionally operates in retained mode, where the DOM exists as an

    in-memory representation of the current UI, and you poke it to make changes

    Since we can't throw away the current web, our library still creates DOM

    elements using our declarative forms. So we're basically operating in an

    immediate mode on top of a retained mode, and I'm starting to think that it

    actually gets us the best of both worlds. React provides "lifecycle" methods

    which trigger at various stages within the retained DOM, which gives you anescape hatch when you need to handle things like focus. Even if it might be

    better for React if there was a lower-level rendering API, just using the DOM

    works pretty well.

    If our library can make edits to the retained DOM fast enough, we can actually

    treat our rendermethods as if they were in immediate mode. That means we

    can implement performance-sensitive things like 60 FPS animations, or a UI

    that changes when scrolling. You may think it's taboo not to use CSS for

    animations, but with requestAnimationFrameand other advancements,

    people are finding out that you can actually use JavaScript for better and even

    more performant animations, as seen with Velocity.js.

    React, with its Virtual DOM, is fast enough to implement animations thatdepend on user input, like scrolling or cursor position. Bloop is dumb and use

    innerHTMLso it's not nearly as good, but on desktop it's at least good enough

    to show an example.

    A wonderful thing about immediate mode is that it's easy to do things like

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    17/26

    varApp = Bloop.createClass({ getInitialState: function(){ return{ pageY: 0, pageHeight: window.innerHeight }; },

    componentDidRender: function(){ varnumItems = this.props.items.length;

    document.querySelector('.list').style.height = numItems * 31+ 'p varul = document.querySelector('ul'); ul.style.top = this.state.pageY + 'px'; },

    render: function(){ varpageY = this.state.pageY; varbegin = pageY / 31| 0; // Add 2 so that the top and bottom of the page are filled with

    // next/prev item, not just whitespace if item not in full view varend = begin + (this.state.pageHeight / 31| 0+ 2);

    varoffset = pageY % 31;

    returndom.div( { className: 'list', style: 'position: relative; top: '+ (-offset) + 'px'}, dom.ul(

    occlusion culling. Another corollary to graphics engines, occlusion culling is a

    algorithm to optimize rendering by figuring out which elements are actually

    visible, and only rendering them. Imagine you have a list of 5000 items. If you

    create a big with all of them, the DOM will grow large, take up lots of

    memory, and scrolling will be degraded (especiallyon mobile). If you know

    only 25 are on the screen at once, why do we need to create 5000 DOM

    elements?

    You should only need 25 DOM elements at one time, and fill them out with th

    25 elements that pass the occlusion test. I made this component in just a few

    minutes to make this work (view the full code here):

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    18/26

    // application code

    varitems = [];for(vari=0; i

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    19/26

    // render

    function render(){ Bloop.renderComponent(app, document.body); requestAnimationFrame(render);}

    render();

    hen the scrollevent is fired, we simply update pageYand pageHeightand

    the new DOM elements are filled with the right data, giving the illusion that

    the user is scrolling down a large list. This basic implementation isn't perfect,

    but it certainly could be with some better edge case handling.

    This is all just with my stupid Bloop library, just imagine what you could do

    with React's optimizations.

    Contrast this with what it would take to implement with Web Components.

    You would have to manually manage all of those DOM nodes yourself, and

    take special care to remove ones outside of the viewport, or even better reusethem and reposition them. Retained mode is a sucky way of doing UIs, and I

    think we'd all be better off if we switched to thinking in immediate mode.

    Additional Abstractions

    Cortex

    e went over how data flows in Bloop and React: data is passed down and

    events are triggered up through event handlers. This is a good way for

    components to talk to each other, but it has drawbacks. Sometimes it's

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    20/26

    vardata = { settings: { username: "James "}, number: 0};

    varcortex = newCortex(data, function(){ // Called whenever an update happened Bloop.renderComponent(app, document.body);});

    varApp = Bloop.createClass({ render: function(){ returndom.div( MainContent(), Settings({ state: this.state.settings }) ); }

    annoying to create many trivial event handlers, and you also wantto be able to

    wrap state management into components instead of it all being top-level.

    There are many ways to improve this, and React actually encourages its

    community to build interesting abstractions on top of React. Cortex is one suc

    enhancement of handling state.

    Cortex is a way to have one single data structure for app state, but have the

    ability to take pieces of it and hand it off to child components. Child

    components have the ability to changestate themselves, and we get update

    notifications. It's basically a type of "observable", but the difference is we don'

    care what has changed. When we get an update notification, we just trigger arerender of the whole app.

    Here's what using Cortex looks like:

    In my component, if I had a Settingscomponent I could pass it down like so

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    21/26

    });

    varSettings = Bloop.createClass({ updateUsername: function(username){ this.state.username.set(username); },

    render: function(){ // ... }});

    And the Settingscomponent could update it like so:

    The callback we passed to Cortexwould be called and the app would be

    rerendered. Now we can do more sophisticated state management, letting

    components manage the state themselves but still having full access to the

    state from the top-level through our normal dataobject. It's like proper Objec

    Oriented Programming!

    Indeed, if you are familiar with functional lenses this sounds all too familiar to

    you. Unfortunately, this is still a mutable data structure, but it still solves the

    encapsulation problem.

    The above code is Bloop-specific. In React, you would need to access the corte

    object in propsin components that get state passed down. Bloop allows you to

    specify a stateproperty when creating the component, and it is used as the

    component's initial state.

    Here is the full increment/decrement example using Cortex (or as a gist):

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    22/26

    vardom = Bloop.dom;

    // components

    varApp = Bloop.createClass({ render: function(){ returndom.div( dom.span(this.state.number.val()), Toolbar({ number: this.state.number }) ); }});

    varToolbar = Bloop.createClass({

    updateNumber: function(value){ this.props.number.set(value); },

    render: function(){ returndom.div( dom.button({ onClick: this.updateNumber.bind(null, this.props.number.val()

    }, 'decrement'), dom.button({

    onClick: this.updateNumber.bind(null, this.props.number.val()}, 'increment')

    ); }});

    vardata = { number: 0};

    varcortex = newCortex(data, function(){

    // Called whenever an update happened Bloop.renderComponent(app, document.body);});

    // render

    varapp = App({ state: cortex });Bloop.renderComponent(app, document.body);

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    23/26

    Bacon.js

    If functional reactive programming (FRP) is your thing, I'm sure you've heard

    of bacon.js. Since Bloop doesn't care where your data comes from, it's trivial to

    use FRP to construct data flows and update the UI whenever something come

    down the stream. This post describes how to do just that with bacon.js.

    Addon: Immutability Helper

    React actually comes with an addon that lets you update data structures

    persistently, the immutability helper. The neat thing is that you can still use

    native JavaScript data structures, but create new objects when performing

    updates instead of mutating them directly.

    It's a little unwieldy to use, however, but with some macro magic it could be

    quite handy.

    Om

    Om is a much more sophisticated abstraction on top of React. It is a

    ClojureScript interface to React that introduces a different way of defining

    components and managing state. Since ClojureScript uses persistent data

    structures natively, app state is immutable and persistent. This immutability

    makes it trivial to check what has changed, since you just have to comparepointers.

    Om uses requestAnimationFrameto render the app continuously batch

    rendering (all requests for rerendering happen just once on the next animation

    frame). If something has changed, it's very quick to detect the components tha

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    24/26

    have changed with a few more pointer checks and rerender those components

    Immutability turns out to be an incredible companion to React, making not

    only app state explicit but also changes over time. Keeping a history involves

    only keeping pointers to previous app states.

    I would love to dive into this more, but that is for a future post.

    Mori

    Mori is a library for persistent data structures for JavaScript. These are same

    data structures used by ClojureScript. If you want a lot of the same benefits

    that Om takes advantage of, like optimized rendering and easy history and

    undo, you can use this library to manage app state as a persistent data

    structure.

    I haven't seen too many persistent JavaScript react apps yet, but there is this

    post about using a different persistent data structure library for building apps

    Finale

    e've shown how React chooses a level of abstraction that is powerful, and

    also adaptive. We haven't explored the details of React's optimizations with

    the virtual DOM, but you can find more about that in the docs and around the

    web. I wanted to focus on the abstract idea itself, and show how well it works

    even with my stupidly simple Bloop library.

    Bloop follows most of React's APIs and conventions, with the following

    differences: in React, the properties object is required when creating DOM

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    25/26

    elements, so you have to pass nullif there aren't any

    (dom.div(null, 'text')). There also is no componentDidRenderin React,

    but there is componentDidUpdate.

    This post assumes Web Components as a way to build applications; some

    people look at Web Components as a low-level way to share custom

    components. Even so, everything is stuck in a global namespace and you miss

    out on all the goodness of modules. Also, it's a hard sell when something like

    React can't even use it, and it's hard to load in components from a completely

    different system, especially when you want to take advantage of what you

    already have.

    There's no doubt that you will need more than this for building apps: we

    haven't mentioned routing, data stores, controllers, and all that stuff. I like the

    ability to choose which libraries to use and see how they are all pieced

    together. See React's post about the flux architecture, a router, and more.

    It's no coincidence that immutability and persistence was repeatedly

    referenced throughout this post. Using persistent data structures with this

    architecture really does allow for powerful features. However, even with

    simple mutable JavaScript objects, React brings a powerful UI and component

    system to the table.

    Read more:

    Bloop

    Source code for all the demos

    React docs

    React provides JSX, an extension to JavaScript, which allows you to embe

    HTML directly in JavaScript. This makes it look like you are writing

  • 7/23/2019 Removing User Interface Complexity, Or Why React is Awesome

    26/26

    Twee0SubmitTweet at meto discuss this post.

    Read Next: Writing Your First Sweet.js Macro

    This is the first entry in a series about writing JavaScript macros with sweet.js.

    You will learn how to write your first macro, basics of pattern matching, and

    how to run the sweet.js compiler and use sourcemaps for debugging.

    Written by James Long, a developer for Mozilla. Get in touch.

    ui react

    HTML, but all it does it transform to the native dom.*calls. I prefer not to

    use it, but it's helpful if you are working with designers.

    Om is a ClojureScript interface to React

    Mori is a library of persistent data structures for JavaScript

    Cortex provides a way to centrally manage data

    Mercury is an attempt to rebuild something like React, separating

    functionality into lots of modules like virtual-dom. It features immutable-

    by-default state and virtual DOM, see more comparison here.

    Mithril is another framework that has similar ideas, and the author has

    commented and ported my examples here.