Skip to content

Instantly share code, notes, and snippets.

@pgoldrbx
Last active August 29, 2015 14:10
Show Gist options
  • Save pgoldrbx/0b6f19fb1bbf058a38ee to your computer and use it in GitHub Desktop.
Save pgoldrbx/0b6f19fb1bbf058a38ee to your computer and use it in GitHub Desktop.
React?

Using React Components in a MVC-like architecture for Client- or Server-side rendering

React

Goals

  • What problems are we trying to solive with React?
  • What are the added benefits of using React?
  • What potential issues or limitations com with using React?

Arguments FOR

  • Geared primarily toward building encapsulated, reusable components.
  • It's fast! Using shadow DOM diff to determine which components require a redraw?
  • Allows for more decoupling between components
  • Simplifies code becuase the speed an efficiencly allows all views to just be re-rendered freshly, rather than having to write controllers and worry about dependencies to update pieces within the hierarchy.
  • React (like Chaplin and Marionette) handles the lifesicle of views and subviews, particularly all the teardown task and cleanup of internal states. Setup and teardown!
  • React adds firm structure (which Backbone, even with Marionnette, is lacking by design)
  • React manages the DOM internally
  • Unidirectional data flow is easier to maintain
  • Does not require a full refactor or switch up-front. Migration can happen over time. React will live happily along-side other views and templates if needed.
  • Plays well with others: there are several ways of connecting with Backbone Models, for example.
  • React components can be rendered on the server side. Any components already rendered by the server will not be replaced whole-sale on the client-side; rather, just the event handlers will be bound if the pre-rendered components are found. HOWEVER: the initial data-set will need to be provided to the client-side as well. It will not be parsed from the current render. It's recommened to do this by dumping it into a script block. This is not elegant but would work fine. Alternatively, the client could make an ajax call during load for the data.
  • React provides jasmine test utilities. usually, the UI layer is the most annoying part of automated front-end testing. React creates makes this easy.
  • Some big companies and communities are using it – so it's hip and cool. Facebook & Instagram, of course, but also Yahoo Mail, Khan Academy, Feedly, Atom!

Arguments AGAINST

  • Learning curve! We've all got deliverable dates!
  • JSX adds another syntax to learn and compile to JS
  • Requires some tooling. Can't just prototype in the browser off the filesystem.
  • The markup (Template) is essentially mixed into the View
  • I don't totally like the implied method naming: this.props.onEventName() calls ancestor::handleEventName(), versus explicit references
  • UI event binding is done directly in the View as component attributes, requiring very direct mapping, versus Backbone Events using a selector/callback map
  • Hapi is already setup to work with Handlebars, especially for server-side rendering. A lot of templates are already written in handlebars and would have to be converted.
  • Cannot use ID attributes on DOM notes -- these are generated internally by React
  • While there are some open-source mixins from the community still appears to be a bit small if my basic searching is any indication
  • Implementation on the server-side still requires some means of string interpolation? That seems to be implied in this post. But it seems that the React components can render to a string and then be passed through a template engine, like handlebars! The current data scope of the template layer maps to this.props?

Inviting Friends to the Party

Flux

There seems to be concern with using Flux on the server-side. But Yahoo has possibly come to the rescue? https://github.com/yahoo/dispatchr

Dispatcher

  • I like the Dispatcher a lot. I like having views decoupled further, not directly updating the model in anyway.
  • I like that the Dispatcher can help manage dependencies between various Stores using the .waitFor method
  • However, I don't feel that the Flux.Dispatcher is the essential means of solving this issue. EventEmitter alone might be sufficient in many ways. Additional, I don't know that unidirectional flow is necessarily essential, though I appreciate the philosophical aspect.
  • Consider omnidirectional flow using events and a pub/sub pattern:

Publishing:

View --> emit('do-something', payload);

Model --> emit('something-happened', payload);

Subscribing:

View --> on('something-happened', function callback(payload) {});

Model --> on('do-something', function callback(payload) {});

Or even more succinctly:

function Bus() {
  this._topics = {};

  this.on = function(topic, callback) {
    if (typeof topic !== 'string') {
      return; // or throw or whatever
    }
    if (!this.topics[topic]) {
      this.topics = [];
    }
    this.topics.push(callback);
  };

  this.emit = function(topic, payload) {
    var i, len, callbacks;

    callbacks = this.topics[topic];
    if (!callbacks) {
      return;
    }

    for (i = 0, len = callbacks.length; i < len; i++) {
      // make this a bit more async friendly
      // with requestAnimationFrame or something
      window.requestAnimationFrame(function () {
        callbacks[i].call(null, payload);
      });
    }
  };
}

var bus = new Bus();
bus.on(topic, function callback(payload)) {
  bus.emit(topic, payload);
}

ANYONE can register a callback. Anyone can publish. Period. Have a look at PubSubJS or others?

Stores

  • Really they sound just like Models. I get that the destinction is that they are not necessarily ORM and can represent state rather than just a record or colleciton of records, but it's basically the same thing.
  • Using Backbone.Model and/or Backbone.Collection instead? This would mean that we're saying in our code "c'mon, it's the same damn thing, so we might as well get all the great functionality from Backbone". This was seen in the "FluxBone" pattern: http://www.toptal.com/front-end/simple-data-flow-in-react-applications-using-flux-and-backbone

Actions

  • The feel just like "Helpers" in Handlebars
  • I feel like it breaks the whole point of using the Dispatcher. Now the view is saying what should be done with data? I don't totally get it. The view should tell the dispatcher about changes coming from the UI, but not what to do with any of this new information... right?

Jest

Testing framework...

@pgoldrbx
Copy link
Author

My "Acceptance Tests" for using React:

  1. Easily setup a workflow (using gulp) to continuously watch and compile JSX to JS
  2. Build a component that can be placed on a page that is encapsulated
  3. Render first on the server-side
  4. Update on the client-side
  5. Write unit tests

Additional:

  • Mix in various data sources - plain object, backbone model, flux store, etc
  • Include as a node module
  • Style using css or sass – ideally find a way to package the style as an include with the component

@pgoldrbx
Copy link
Author

Before beginning to build my sample to meet the acceptance tests, here is a hypothetical implementation:

Server

  • Handlebars for all static page template elements
  • React Component(s) --> rendered to a string --> Handlebars --> Hapi
var React = require('react');
...
var markup = React.renderComponentToString(
    MyComponent({ foo: 'bar' })
);
res.render('template', {
    markup: markup
});
<div id="container">{{{ markup }}}</div>

Client
React Components --> attach to components previously rendered by the server --> business as usual

Reference
http://www.princeton.edu/~crmarsh/react-ssr/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment