Hi, I'm just reading through the ReactJS docs for the first time, and I found something peculiar. Let me explain.
So, as I understand ReactJS invokes a render() method defined by the user (programmer), and the result of that is diff'ed to previous results of the same method, and as a result, the DOM is modified.
I can understand this, but... The DOM is already incrementally rendered by the browser. That is, changes to the DOM are not direct, but are performed by the browser in a way that is as efficient as possible. So, why the need for a render() method?
Also, the render() method itself could be quite big and thus could take a long time to run, and almost completely nullify all advantages of incrementally updating only the display.
> I can understand this, but... The DOM is already incrementally rendered by the browser. That is, changes to the DOM are not direct, but are performed by the browser in a way that is as efficient as possible. So, why the need for a render() method?
The browser tries to batch updates as efficiently as possible, but because of the model there are limits on that, reflow and layout thrashing are easy pitfalls to fall into for instance. React's model obviates these issues so the system can make significantly more assumptions about what is happening, and it can combine and batch more aggressively.
Furthermore, React's updates can trivially be delayed as much as necessary (in browsers, usually to sync them with the next repaint using requestAnimationFrame), not so for DOM mutations.
> Also, the render() method itself could be quite big and thus could take a long time to run, and almost completely nullify all advantages of incrementally updating only the display.
It could be but it usually isn't, and more importantly for these cases (and others) the system can be told that there's no need to re-render the component (`shouldComponentUpdate` in react), the previous result can be reused as-is.
For "pure" components (which simply map application state to render state) it's easy to implement, and downright trivial when using persistent datastructures: it's just an identity check (which is why systems built from the ground up on persistent datastructures like clojurescript/om or elm.html are faster than "naïve" react OOTB: they can assume components are pure, will only use their parameters/state to generate UI state, and that said parameters/state hasn't been screwed with; React being a more general-purpose JS library can't afford to make these assumption, it has to be opted in explicitly)
There are added benefits for an immutable / FRP style of building a UI, but I think you're more interested in the rest of my answer.
Nothing to be confused about, you have a valid hypothesis: "Rendering and Diffing in Javascript is slower than DOM manipulation". However, all hypothesis need to be experimented before having any real meaning.
It just happens that testing this hypothesis and finding it invalid was the foundation for React.
So let's assume I have a listbox with N elements in it. I assume the render() method has to visit each of these elements every time an updates takes place, so it is O(N). Please correct me if I'm wrong here.
Now for a certain value of N, things will become slow, unresponsive. I'm guessing now that this value of N is above the value where the browser cannot handle this number of elements anyway. Is this correct?
PS: I understand that it is possible to completely optimize the listbox case. But that is not the scenario I'm interested in. I'm interested most and above all in the typical use-case. I'm just using a listbox as an example here. Just replace it by any other sufficiently complicated and customized UI component if desired.
A render in react doesn't return DOM elements. Render is a pure function which projects app state into a description of the view. Let's call this description A, so render(state) -> A. When state changes and render is called a second time we get a new description, render(newState) -> B. A diff is then applied to them diff(A, B). The diff can then be translated to an update strategy for the actual DOM (or in this case, native components). From this the smallest set of changes are applied to bring the DOM in line with the current state. It turns out it's far quicker to diff objects in memory than query the DOM. The DOM is treated as a stateless rendering engine, with progressive updates applied as app state changes.
Of course, in your example, there is a point where N gets so large that it poses problems. This is true whether you're using the raw DOM or an abstraction like React. The solution is the same in either case - cull non-visible elements from the render tree (again react makes this as efficient as possible).
EDIT: it's also worth noting you can skip updating entire subtrees in React with shouldComponentUpdate hooks. This avoids the case where you go through the render/diff cycle and it turns out there is no difference, and so nothing to update. Paired with immutable data structures this method can be a simple reference equality check (shallow equals) oldState !== newState, and therefore can really get the most efficient rendering possible
Amelius, I cannot reply to you directly for some reason. Anyway...
So say you're rendering some long list of items. Because react is just plain JS (no templates!), you will map the array of items to components in your render. You give each item a key so that react can reconcile components across renders. If component with same key is returned from render next time then it's kept in the DOM (and any updates applied), if not then it's removed. So the involved part becomes determining what is visible or not, and then slicing your list of items to only map a subset. This is the more involved part. I haven't built anything like this myself, but I'm guessing it basically boils down to: listening to scroll events on container, determining y offset, get height of contaoner and height of elements, use that to determine which items in list should be mapped to components in render. That bit is the same whether react or not.
See James Longster's epic blog post "Reducing User Interface Complexity, Or Why React Is Awesome" [0]. Where he details this kind of approach with his for-demp-purposes-only react-like lib.
render() -> returns N items every time it is invoked
And React will diff those N items against the DOM (using a key) every time an update is required.
So, to me it appears that still O(N) amount of work has to be done every time something small changes in the list.
This is not necessarily a bad thing. But here we assume that doing render() is faster than actually building the DOM from scratch, even though they have the same computational complexity, O(N).
Anyway, I'll have a look at that link. Very interesting, thanks again.
no, it doesn't compare to DOM, it compares to the last result of render - an important difference performance-wise. the DOM is many times slower to query than plain objects.
also remember big-O notation doesn't really capture the essence of the problem here. looping through and querying N DOM elements is probably orders of magnitude slower than doing the same with N objects in memory
But how do I remove non-visible elements from the render-tree? Is this something I have to do explicitly like I would have to do without React? Or does React somehow take care of this for me?
Swapping out inner html has issues such as losing form data and bound events (etc...). Ignoring that you also have life cycle issues. For instance if you have a component that needs to know its render height you want a hook for calculating that height AFTER the entire DOM has updated. Otherwise you are going to force multiple layouts (bad). React provides these life cycle methods.
As far as the render method being big, the premise React is built on is that the virtual DOM is much faster than the real DOM. Test as necessary for your own comfort / use cases. I personally trust the testimonials of people smarter than me that aren't the React team also feel this is true and are making real changes to their products based on that. I also trust my own experiences that the DOM is slow :)
DOM is stateful, whereas the render() method is stateless.
For instance, if you add a <button> to the DOM, it will stay there forever. On the other hand if the next call to render() no longer includes the <button> then React will do the work of removing it from the DOM.
There are two things that stop it from being slow in practice:
1. The virtual DOM that render deals with is several orders of magnitude faster than the real DOM, so you need to have thousands of elements before it starts taking longer than making the one change to the real DOM for the one element that changed
2. You can skip calling render on subcomponents that haven't changed, so while updating a list of 10000 elements where one has changed would require iterating through 10000 elements, updating a list of 100 elements that each contains 100 elements where one of the bottom level elements has changed would only require iterating through 200 elements.
So, as I understand ReactJS invokes a render() method defined by the user (programmer), and the result of that is diff'ed to previous results of the same method, and as a result, the DOM is modified.
I can understand this, but... The DOM is already incrementally rendered by the browser. That is, changes to the DOM are not direct, but are performed by the browser in a way that is as efficient as possible. So, why the need for a render() method?
Also, the render() method itself could be quite big and thus could take a long time to run, and almost completely nullify all advantages of incrementally updating only the display.
So, as you can see, I'm a bit confused...