Live data from Hacker News

React Implementation Notes

facebook.github.io

81–90 of 93 posts

Re: React Implementation Notes

#81

Earlier quoted context omitted.

The Virtual DOM is a central part of how React works, so it does make sense to mention it when appraising React.

FWIW we are trying to avoid "virtual DOM" in the new docs. The thing you create in render() and that describes the tree has been called "a React element" for many versions by now. "Virtual DOM" was more of a marketing term and I find it misleading because it doesn't make sense with e.g. React Native, and also makes React seem like a performance trick. React is not a performance trick. That elements get compared by Re…

Thanks for pointing that out. The term "DOM renderer" does seem much clearer than "virtual DOM".

Re: React Implementation Notes

#82

Earlier quoted context omitted.

I understand your dilemma very well. I have experience building server-rendered web apps, web apps enhanced with jQuery, Angular apps, and React apps. I resisted React for a while, but I'm glad I finally tried it out. Compared with jQuery or purely server-rendered apps, React elevates what you can accomplish within a short time frame. A complex web form with async validation, client side calculations, and multiple br…

The virtual dom is an implementation detail, it doesn't make sense to sing its praises here.

If I wanted to do what React does, but without React, I might start like this:

document.innerHTML = myComponent.render();

That way, I can write components similar to React components and it can render very quickly. However, this strategy would work only on the initial render, because this naive strategy would destroy implicit DOM state like scroll positions, focus state, and cursor selections. The React DOM renderer (formerly known as the virtual DOM) lets me apply this rendering strategy without ruining implicit state.

The DOM renderer does not give me components or performance, since I can already achieve those things with raw Javascript. What it gives me is the ability to use a simple, clean rendering strategy without destroying the DOM state. That's why the React DOM renderer is not just an implementation detail.

Re: React Implementation Notes

#83
post #77

Earlier quoted context omitted.

I don't know what you mean by "it doesn't really matter." Surely there's a cost to using generators instead of regular function calls and returns, right? That's where the overhead would come from, because generators aren't free. Right now, Pyret and GopherJS (the last time I checked in GopherJS's case) basically manually encode the `IteratorResult` type, and check for "stack unwind" vs "regular result" when each func…

Ah! You're right. Now I get what you mean. Thanks.

No problem. Thanks for asking bluntly about generators. It made me write some more experiments using them.

Right now, the main thing that I think stands in the way of them being a good solution for Pyret is that they have limited stack depth – about 7000 frames on Chrome Canary, for instance. One thing we get out of our strategy, which reifies stack frames onto the heap when pausing, is that we can simulate a much deeper stack.

See http://imgur.com/a/GaBg2 (I don't need to be able to do sum of ten million, but 10,000 would be nice! This is just so a non-tail-recursive map works over reasonable-sized lists; we'd rather not have the concept of tail recursion as a curricular dependency for working with that size of data.)

Re: React Implementation Notes

#84
post #38
post #11

Part of these notes, Fiber [0], reminds me of a half-joking "corollary" to Greenspun's Tenth Rule (credit @shriramkmurthi): Any sufficiently complicated JavaScript program contains an ad hoc, informally-specified, bug-ridden, slow implementation of delimited continuations. Control over continuations and the stack is our main compiler and runtime engineering hurdle in Pyret. Projects like Doppio [1], WeScheme [2] and…

Offtopic but... I'm cheering for pyret! And for the group behind it of course. :)

Thanks for the good vibes!

Re: React Implementation Notes

#85

Earlier quoted context omitted.

What about generators? I've built coroutines based on generators. CSP has been implemented in JS using generators. Not sure why those projects went thru "staggering amount of overhead" to get what we get for free with generators. Please educate.

Because generators may be implemented with delineated continuations but they are not themselves the same thing.

that's Delimited continuation

Re: React Implementation Notes

#86
post #83

Earlier quoted context omitted.

Ah! You're right. Now I get what you mean. Thanks.

No problem. Thanks for asking bluntly about generators. It made me write some more experiments using them. Right now, the main thing that I think stands in the way of them being a good solution for Pyret is that they have limited stack depth – about 7000 frames on Chrome Canary, for instance. One thing we get out of our strategy, which reifies stack frames onto the heap when pausing, is that we can simulate a much de…

Pyret looks like a good endeavor.

Another dumb question: doesn't every recursive function have an iterative version? and in the case where you're transpiling to JS couldn't 'map' (whatever the syntax in Pyret) and other functional primitives be converted to imperative code? Generator functions are no different than regular functions when it comes to stack depth. I think that depends on the amount of memory you have, so different from machine to machine. I'm learning by asking dumb questions... :)

Re: React Implementation Notes

#87
post #83

Earlier quoted context omitted.

No problem. Thanks for asking bluntly about generators. It made me write some more experiments using them. Right now, the main thing that I think stands in the way of them being a good solution for Pyret is that they have limited stack depth – about 7000 frames on Chrome Canary, for instance. One thing we get out of our strategy, which reifies stack frames onto the heap when pausing, is that we can simulate a much de…

Pyret looks like a good endeavor. Another dumb question: doesn't every recursive function have an iterative version? and in the case where you're transpiling to JS couldn't 'map' (whatever the syntax in Pyret) and other functional primitives be converted to imperative code? Generator functions are no different than regular functions when it comes to stack depth. I think that depends on the amount of memory you have,…

> doesn't every recursive function have an iterative version

True in the abstract, yes. But it's a sophisticated compiler indeed that turns something like a recursive binary tree traversal into a loop (it would need to synthesize the stack worklist).

In practice, it's easy to do this for tail recursion (and mutual tail recursion, with a little more sophistication). You can get slightly fancier with "tail recursion modulo cons," which is a little more clever and handles map. Beyond that, it's pretty gnarly to do a good transformation, because recursive code is implicitly using the stack in interesting ways.

> couldn't... functional primitives be converted to imperative code

Indeed, and we do write those in pure JS with carefully-crafted while loops to make those primitives more efficient. But if students are learning to write their own map, or another functional combinator on lists, those need to work, too, and will be implemented recursively by them.

> Generator functions are no different than regular functions when it comes to stack depth.

Yeah, stack depth in general is annoyingly low on modern browsers, IMO, so this isn't just a problem with generators. It's also unpredictable (http://stackoverflow.com/a/28730491/2718315). So we're working around the normal stack limit already. I was sort of hoping that when a generator's continuation was captured, it would stay heap-allocated and not "count" towards stack space when restarted, but that's not the case.

Re: React Implementation Notes

#88
post #13

I have a question, lack of dynamic scope in render functions is causing me major issues in making highly dynamic and complicated UIs in ClojureScript. I understand that Javascript doesn't have proper dynamic scope so you guys probably weren't thinking about it, but I also see you guys moving farther and farther away, there's the whole Context hacks and now the web worker call stack serialization thing. Are there tech…

I don't know much Clojure, but it sounds like this is exactly the use case that context is meant for. If you were to use dynamic scope, how would the variables be restored if one of the descendants updated via setState? It doesn't seem to me like it would work. Also, not sure what "context hacks" you mean or what "web worker call stack serialization" is.

I think dynamic scope can work with setState by allocating a new closure that closes over the dynamic vars, aliasing them into lexical scope of the render function.

The problem with React Context is that it bypasses the general solution offered at language layer for a react-specific solution, essentially breaking any code written without prior knowledge of React.

Re: React Implementation Notes

#89
post #13

Earlier quoted context omitted.

I don't know much Clojure, but it sounds like this is exactly the use case that context is meant for. If you were to use dynamic scope, how would the variables be restored if one of the descendants updated via setState? It doesn't seem to me like it would work. Also, not sure what "context hacks" you mean or what "web worker call stack serialization" is.

I think dynamic scope can work with setState by allocating a new closure that closes over the dynamic vars, aliasing them into lexical scope of the render function. The problem with React Context is that it bypasses the general solution offered at language layer for a react-specific solution, essentially breaking any code written without prior knowledge of React.

Can you say what changes would be required in React for this to work? I assume that you are asking for parent components to be on the stack when child components render, but that is simply not how React works – you return a description of what you want to render then React calls into the children. How could the parent be on the stack?

If you have a concrete suggestion for something we could change in React I'd be happy to chat about it. I'd love to support CLJS better.

Re: React Implementation Notes

#90
post #57

Earlier quoted context omitted.

I think redux got 50% right and 50% wrong. 1. good: having a sideeffect-free, serializable application state. That concept can also be used in mobile apps for easy pause/resume. 2. good: being able to render that state from one parent, because you don't need to manage two separate statemachines (the UI and the business logic) anymore. 3. bad: shoe-horning all state-transition into functions/reducers. Fp is awesome, b…

My main hesitation with Redux is how awkward it is to insert async calls in the chain of events. Things like redux-thunk and redux-saga are impressive hacks but feel like a lot of gymnastics to do something that pretty much every React app needs to do.

I dislike redux-thunk, because it breaks serialisability. I don't know redux-saga, but this is my way of doing it:

Create a data-structure like "running requests" in your state and put a serializable object in, could be complex or just a string "GET_USER_DATA".

Then write a simple "NetworkManager". React reconciles the UI to match the state, the NetworkManager does the same for your "running requests": make the actual running requests match those in the state.

In some projects I even (ab)used React to do that: add a component that renders a and manage the XHR in componentDidUpdate etc.

    
        state.GETsuccess(...)}>
    
This way everything can be paused and is easy to reason about. You get cancellation and reuse of requests for free by using keys.
Post reply on HN