Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

My team has been using React full time since half January. What felt like a gamble initially, turned into mad love. I understand why people still use e.g. Angular, but only for the same reason as why people still use PHP: legacy. React is simply that much better. I hope its ideas will spawn many followers.

Our design/css/markup person loves it, because with JSX she can trivially write familiar HTML, and at the same time she's highly encouraged to make little reusable building blocks (components, in React lingo).

The result is that our view codebase is remarkably well structured, even though very little of it has been touched by seasoned hardcore programmer types. React pulls us into this "pit of success" of locality, reusability and clarity.

I'll braindump what we do, for those interested, just to share an alternative approach as the one listed in this blog. I'm not sure whether it's better or worse - we're probably doing some things in ways that could be better, so I'm very open to questions and critique, too.

Our frontend architecture is Model-View-Nothing. Our view is pure React components, with a root component called View which has, as its state, the current page and the model. We also have a thin "proxy" with a function for each backend API call, but it has no state so it's not very interesting in terms of architecture.

The model is a simple hierarchy of classes. We use TypeScript for these classes, but it could've been CoffeeScript, ES6 or JS-hacked classes as well, no difference. Our root model class owns all other model objects, hierarchically.

A user action typically causes our model to want to extend or update itself. It does API requests to our backend for this. In the case of PUT/POST/DELETE, we update the local model first, and then asynchronously send it to the server - our data is such that we can assume that most of the time, the operation will succeed. This is great, because we can re-render the view before the server even acknowledges the operation's success. This makes the app is very snappy.

We even do the same with GET requests: very often, we already have some of the data we're getting (e.g. if you go to a user profile, you typically clicked a link with that user's name and avatar on it). When it makes sense, we just show the page immediately with what data we have, and show a little loading animation for the rest. Again, this makes the application feel very responsive.

This works for us without any additional tools, because our model layer somewhat resembles the structure of the view. I suspect that this is quite common (e.g. if you're displaying a list of tweets by a user, you probably have a "root -> user -> list-of-tweets" structure both in the model and the view). It allows us to pass only the model data to a React component that it needs, making our React components very slim and reusable.

In effect, our model layer is one big client-side cache. The methods on the model deal with synchronizing this cache, and the view just re-renders whenever the model tells it to. Another benefit is that if users navigate to pages they've seen recently, we still have all the data, and can show it right away (and refresh it in the background).

All of this works great on mobile too. Especially when viewing a page you saw before (e.g. by hitting "back"), the difference is enormous: many mobile browsers cache very little history (at least my Chrome doesn't do it much), but for our page, it's instantly there.

We would've gone for this dynamic no matter what, because we want to make a web app that feels like a native app. Nevertheless, I believe that React allowed us to do it with amazing simplicity and effectiveness. Other tools, such as Spine[0] encourage the same asynchronous dynamic, but I believe that without React, we would've spent a lot more time fighting with state, asynch timing issues, and all that.

[0] http://spinejs.com/



This is a similar approach to what we're doing, except I went with Ractive for the view layer instead of React.

How do you handle failed PUT/POST/DELETEs and cases where the user is offline? This has been our biggest hurdle; mainly because when users feel like what they're using is a desktop app they think less about internet connectivity and assume the app should work offline (oy).

Currently I have the model save revisions of its data (one large document) through pouchdb but any solution with offline capabilities at some point needs to figure out conflict resolutions. I haven't got too far into conflicts/diffs but I'm curious if that's something you guys dealt with.


We don't really deal with it at all yet. Good warning! That said, given the nature of our app, I think that for us, displaying an error bar on top and caching the last PUT/POST/DELETE in a slow retry loop is probably enough - it's a content-heavy app, so people will understand that the content comes from the internet. I hope. :-)

So, in short, you're ahead of us here.


> I understand why people still use e.g. Angular, > but only for the same reason as why people still use PHP: legacy.

Not an Angular lover, but devs use Angular because it's the quickest way to build LOB clients with javascript. It's no legacy or whatever,just pragmatism. People coming from flex/wpf know what I mean.

Angular doesnt answer all problems and is not for all use cases but for LOB clients it handles 90% of use cases and problems. Same cannot be said for React without 3rd party libraries.

No framework can ever be a silverbullet, I would not use Angular for an app with a lot of transitional states and animations.

In theory you could use React with Angular but frankly I dont see the point.

> but only for the same reason as why people still use PHP: legacy

? You just cant compare apples and oranges, a javascript framework and a serverside scripting language.


Angularjs first release was about 6 years ago so it's hardly legacy. It's almost as if the OP was trying to plug React..

Anyway, in my experience angular is half the code, half the time, and half the cost.

http://www.google.com/trends/explore#q=Angularjs

Well will you look at that, angular seems to never get less popular.


> In theory you could use React with Angular but frankly I dont see the point.

The idea of react as a better compositor for angular keeps iterating back around in my head, which makes me wonder if a sort of 'final answer' will turn out to be somewhere between the two - a $watch isn't that much different to a reactive stream if you squint, after all.


>we can re-render the view before the server even acknowledges the operation's success

This can be done in angular too, but I don't understand why you would want to give someone the assumption that an operation was successful before you know if it actually was.


because many operations will be a success 99.9% of the time and it's often possible (and better overall UX) to handle the error case gracefully a second or two later.


Yep, 'optimistic updates' is the term I like. Alex Maccaw wrote a good article on the subject a few years back:

http://old.alexmaccaw.com/posts/async_ui


How do you handle URL routing (what happens when I click "view tweets" on a user page) and generation (how do you generate the src attribute on the "view tweets" `a` tag)? Are you using anything special for handling URL history and push state?


We're using Director, like daGrevis in the other comment. Currently, our URLs look like http://devlocal.izooble.com/#profile/someone but adopting the History API (where available) for dropping the # is part of the plans.

Currently, Director is configured inside in the root view component, and at every URL change, we do a setState with new page information and relevant parameters. Then, in render() have a big switch over all accepted page names and render the appropriate SomethingPage component with the appropriate props (usually the root model object and some parameters from the URL).

I feel like there must be a more elegant way with less repetition (if you go to /#profile/moo, we do setState({page: "profile", ...}), and in render we check if this.state.page equals "profile", to instantiate a ProfilePage - that's 4 times the word "profile" if you include the URL, a bit too often for my taste). But it's just boilerplate and it's local to one file so we can live with it for now.

Open to ideas though!

As an aside, React runs great on Node; we don't touch the DOM or browser APIs anywhere at all. One more plan that we have is to run our entire frontend on the backend too, once we drop the "#". The idea is to just have it call the "real" backend (the API) like it would always do, to render the very first page for a URL. Great for SEO, too, and no need for hashbangs or duplicate view-rendering code.

I'd be very interested if anyone has any experience using React to render initial page layouts like this.


>I'd be very interested if anyone has any experience using React to render initial page layouts like this.

I'm actually working on an app architecture (within a toy project) that does just this; it uses react-router-component for the routing (so routes work both on both ends without environment specific stuff) and the api is built with racer (https://github.com/codeparty/racer) so that data access on the client or server is unified too

Repo: https://github.com/gnijor/Reaxer

Toy app in question: http://reaxer.nodejitsu.com/ (also contains a keyframed animation written as a react component for fun)


> I feel like there must be a more elegant way with less repetition

That doesn't actually seem that bad, if you consider that half of that repetition is so that you have distributable URLs that can be opened up on another browser thousands of miles away with no other state information.

You could just have an anchor tag with no src attribute, but with onclick=setState(...). That removes the repetition, but you don't get a shareable URL.


I'm doing something similar[0] like author is doing and for URL routing I use Director[1].

[0] https://github.com/daGrevis/Hendrix

[1] https://github.com/flatiron/director


Interesting. Can you tell a little bit more about your 'hierarchy of models'? How does state trickle down to child-models, etc?


It's a pretty traditional data-heavy class diagram, it looks pretty much like the kind of class modelling they teach you in college. Our site is about product reviews, so we have users and reviews and products and stuff like that as aggregate roots. Reviews can have comments, users can have profiles, etc etc. A big composition tree, really. We avoid storing the same data twice (and thus, sometimes outdated) by unique'ing the aggregate root objects by their id.

I don't completely understand your question about state trickling down, however, so I'll just blarb another braindump.

Let's assume a page where we render a product review. A Review react component gets, as a prop, an instance of a Review model object. If a user writes a comment on a review, then a Review react component fires an onNewComment event (also a prop), which we tie to an addComment method on the Review model. This model stores the change, sends a POST to the backend asynhronously, and returns a trigger to the view that something changed. The view does a full page-wide rerender (which is fast because React).

We keep all "data" kind of state in our model, all addressible via a core Model object (of which only a single instance will ever exist). The entire tree of React components is built up with props, no this.state at all, except on the root React component (that we call View).

One exception to that rule is state that is only there to help the user, and not "real" data. For example, whether or not some panel is collapsed, or whether or not a few items are selected for a group operation, fancy effects, etc. The kind of things that we're OK with forgetting if the user would navigate away from the current screen. That stuff we do as locally as possible in a component with setState.

I'm not sure to what extent all this is a best practice though. I talked to the React guys on IRC (they're very approachable and very kind!), and they say that a global refresh and preferring props over state is what they do too, so that's something at least :-)

Did this answer your question?


I think it did answer the question. Very nice overview!


splendid! Going to cook something up. Cheers




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: