Bsaltpack 4 =~i|(~ -g^aj^ԁLE6@t web server -> router -> API calls -> resolve API calls -> render CLIENT: link click -> router -> API calls -> render -> resolve API calls -> render again ``` The server and client aren't that different - the only glue code necessary is: * Build a "fake" http request object on the client that matches the web server, so the router doesn't care what environment it's running in; * Abstract out _synchronous_ data-loading on the server from _asynchronous_ data- loading on the client, so that both environments can operate efficiently. The server shouldn't render pages until it has all the data it needs, but the client can render immediately and update the view as data loads. I designed [horse](https://github.com/reddit/horse)[2] and a react-specific subclass, [horse-react](https://github.com/reddit/horse-react)[2] as the middle layer, which looks something like: ``` http request > middleware >v v v<<<<<<<<<<<<<<<<<<<<<<<<<<< v (wait if synchronous) router > middleware > route > register API calls > render template ^ (watch for updates if async) ^<<<<<<<<<<<<<<<<<<<<<<<<< ^ link click > middleware >^ ``` I also built an API library, [snoode](https://github.com/reddit/snoode)[3] to make dealing with Reddit's _[unique api design](https://reddit.com/dev/api)_ easy, [feet](https://github.com/reddit/feet)[4] for feature flags (turn X on if you're an employee, or turn Y on if there's a querystring flag), and [restcache](https://github.com/reddit/restcache)[5] to handle the caching of REST-like API responses. With this done, building new pages that rendered server-side and client side looked like: 1. Write a new route handler, mapping a URL to an asynchronous function. It should set props on the request context object, which will later be passed into a React template, register any API calls that need to be made, and set its page property to a React class. The page will be rendered inside of the layout, with the props you set up in the handler passed into it. [Here's an example.](https://github.com/reddit/reddit-mobile/blob/master/src/routes.jsx#L576-L608) 2. Write your React template and any necessary components. 3. Write any styles you need. 4. Build it, and :magic: This worked for quite some time, and it was very straightforward to add pages. However, as the team and app grew, we noticed a few areas where things got a little messy. It was never clear how best to handle interactions, and especially interactions that took place across components. You could use one of a couple methods, two of which we used heavily: * Create a handler function in the parent element, and pass it into the child element. For example, you might have ``, where `postComment` is owned by the element which contains `CommentSubmisisonForm`. The trouble is, you'd have to duplicate this comment submission function for any element that contains `CommentSubmissionForm` - for example, you can post a comment from either the `Post` itself, or as a reply to another `Comment`. You could subclass both `Post` and `Comment` from a superClass like `CommentReplyable`, but multiple inheritence doesn't exist in Javascript. You could write mixins for classes that append methods to the prototype, but then you still have to pass the code from some level of parent through to some level of child; and all elements in between the component with API access and the submit button itself have to be aware of the heirarchy. It isn't clear exactly _who_ should be making the API call. This lead to a lot of inconsistency. * Create an event. Fire off `app.emit('newComment', commentData })`, and have a listener that watches for that event, submits an API change, then emits a `success` or `error` function. Again, problem is - you have to write code in both places that watch for that `success`, and remember to stop listening when your element is discarded because you moved on to another page. In either of these cases, you're required to pass in API information, such as your current token, from the top-level all the way through to whichever component actually makes the API call. This gets even more complicated when you have to start pausing API requests when refreshing your OAuth token. Eventually, things got to where we couldn't answer the question of handling intra-component interactivity without taking a second look at how our framework was designed. Stand by for Part II, whereupon I discuss our use of Redux to solve these issues. [0] since I began this post, Meteor switched to rendering React and Angular directly on the server, rather than either no server-side rendering or spinning up a WebKit project, and is thus no longer a Poor Choice. [1] I've always dislked Angular because it shoves a ton of magic into the template layer, and you don't have any visibility into what's going on. I dislike Magic. [2] Horse is now deprecated, in favor of [node-platform](https://github.com/reddit/node-platform). [3] Snoode is now found at [node-api-client](https://github.com/reddit/node-api-client) [4] Feet is now found at [node-flags](https://github.com/reddit/node-flags) [5] Restcache is now defunct, replaced by a Redux store. @aSpq#~޴o3ߣb;| kftPKS'rPP