3 ms·
> we did an experiment allowing the entire DOM to rerender from a top-level React component when the underlying data model changed. That's really not how you'd
by Lazare 11y ago
> we did an experiment allowing the entire DOM to rerender from a top-level React component when the underlying data model changed.
That's really not how you'd write a large idiomatic React application.
Conceptually, you should be running render() methods on the actual items which have changed, and a very small number of parent components with little-to-no DOM elements. You should also be executing a relatively small number of shouldComponentUpdate() methods which should be extremely fast to execute.
If you are doing that, then where exactly was the performance bottleneck, and how was your alternative approach avoiding it? I mean, sure, if there's simply no possible way for you to figure out what to re-render cheaply, then your app will have poor performance. But that doesn't seem to be true from your other comments.
In fact, it sounds like your second approach might be closer to a normal idiomatic React app than your first implementation. Incidentally, are you using some sort of flux architecture?
- Silhouette 11y agoThat's really not how you'd write a large idiomatic React application. That's a bit No True Scotsman for me. Just in the replies to my previous comment, there are several quite clear yet apparently contradictory views expressed about how these things are supposed to work. I doubt that anyone, probably including the React developers themselves, yet has enough experience to sensibly determine what is and isn't idiomatic or best practice here. In any case, I'm far more interested in effective results than dogma, which is why we've been doing practical investigations into what really happens when the tools are used in different ways. Conceptually, you should be running render() methods on the actual items which have changed, and a very small number of parent components with little-to-no DOM elements. You should also be executing a relatively small number of shouldComponentUpdate() methods which should be extremely fast to execute. And this is where the assumptions started breaking down in our experiment. For one thing, we have a somewhat complicated data model and a somewhat complicated UI to present it. Managing the relevant shouldComponentUpdate mechanisms was getting tedious even in our short term experiment. If you're rerendering top-down in a heavily nested UI then it seems you need to provide shouldComponentUpdate in quite a lot of places just to deal with the parent-child relationships with acceptable efficiency, and each of those implementations is a potential source of errors because now you have multiple sources of truth. Also, for things that aren't extremely fast to execute because they rely on derived data, you wind up with questions of what intermediate results to cache and where. Caching intermediate results goes against the kind of approach much of the React community seems to advocate, hence props in getInitialState being an antipattern and all that. Time will tell, but once the abstraction started leaking in these kinds of ways, it wasn't clear to us that trying to follow the pure, declarative approach that a lot of React advocacy promotes was really better than other strategies. If you are doing that, then where exactly was the performance bottleneck, and how was your alternative approach avoiding it? Bottlenecks were as above, among others. Avoiding them comes down to the simple principle that if each component attaches to listen to events in a modular way and sets its own state accordingly, then the default is essentially not to rerender anything and has zero overhead. As soon as you start making active decisions about what to rerender, you immediately have the overhead of those decisions to worry about, and sometimes that overhead is significant if you're, say, responding to every character typed in a text box. If you respond at a more local level, you can choose the granularity of responses to keep the overheads more controlled, which it seems so far is effectively getting the best of both worlds. In fact, it sounds like your second approach might be closer to a normal idiomatic React app than your first implementation. Incidentally, are you using some sort of flux architecture? In the sense that user interactions send meaningful messages to an internal component, that component is responsible for updating some stored data accordingly, and the internal component then signals changes that other parts of the UI can respond to, yes, it's somewhat like Flux in overall architecture.
- Lazare 11y agoAh, I understand now. It's always been clear that React alone isn't enough to write a full application; you need to combine it with some other pieces. The official recommendation from Facebook is to use flux, and the most popular flux implementation (redux) is quite good, but you can also use Om-style approaches (Omniscient, Baobab, etc.), or wire it up to legacy Backbone models, or whatever. But you need something; you can't write an MVC app with only the V and expect good results. You guys tried to write a large "pure" React application, and it failed in all the ways that large React applications always fail. And you learned from it, and hacked together a kinda-sorta-fluxish solution, and it's working for you, and that's great. ...but if you want to take another look at it someday, I'd highly suggest checking out redux. It's very slick, and it was explicitly designed to solve the exact issues you ran into. (In particular, a redux app is built of a small number of smart components which respond to to a specific subset of events, each managing a tree of "dumb" components, which gives you the granularity you were looking for. And then you can use the PureRenderMixin and immutable data, which gives you the very fast shouldComponentUpdate methods you were looking for for free, no need to ever write your own since, as you found, that way lies madness. The docs even cover how to properly cache derived data, another issue you noted running into. And so on.)
- Silhouette 11y agoSorry, but I think you have completely misunderstood me. We're not avoiding the popular Flux-style tools like Redux because we don't understand the need for a model behind the rendering components. We're avoiding them because they are nowhere near powerful enough out of the box for our data modelling and constraint enforcement needs. We're not kinda-sorta-fluxish, Flux is kinda-sorta where we were about five years ago on the Web side and probably closer to a decade ago if you include applications written in languages other than JS for native clients and the like. And we don't want to write a full application in React; we already have 90% of the application and we're just experimenting with whether React might offer a cleaner architecture for the easy bit in our next version.
- Lazare 11y ago