
React Implementation Notes - bpierre
https://facebook.github.io/react/contributing/implementation-notes.html
======
ggregoire
Bravo to the React team for the work during the last few months! It's truly
amazing, specially for the new users.

Among other things:

1) Create-React-App[1]: Lets everyone starts a React app with Babel/Webpack
(JSX, classes and import/export) without any knowledge about Babel/Webpack.

2) The new Contributing docs (Codebase Overview, Implementation Notes
(discussed here) and Design Principles) offer a starting point to figure out
how React works.

3) A revamp of the current documentation is going to be released soon[2]

4) "You might not need Redux"[3]

\---

[1] [https://github.com/facebookincubator/create-react-
app](https://github.com/facebookincubator/create-react-app)

[2]
[https://github.com/facebook/react/pulls?q=is%3Apr+is%3Aopen+...](https://github.com/facebook/react/pulls?q=is%3Apr+is%3Aopen+label%3A%22Component%3A+Documentation+%26+Website%22)

[3] [https://medium.com/@dan_abramov/you-might-not-need-redux-
be4...](https://medium.com/@dan_abramov/you-might-not-need-redux-
be46360cf367#.f1q1ro20m)

~~~
guscost
> 4) "You might not need Redux"[3]

For some older projects I found a hand-built flux helper[0] of ~220 lines
worked great and didn't require any toolchain modifications. The switching
costs to some of the ES6 stuff was too high in that case, but it's also a
potentially leaner app if you don't need the advanced features of Redux and
ES6.

The Redux architecture is so nice though.

[0] [https://github.com/guscost/simple-
flux](https://github.com/guscost/simple-flux)

~~~
cel1ne
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, but sometimes imperative constructs are more appropriate, especially
in a mostly imperative language. Use fp where it feels natural, not
everywhere.

4\. bad: shoe-horning everything into immutable data-structures.

Seriously, when did

    
    
        newName => {
           this.name = newName
        }
    

become

    
    
        (state, newName) => {
           return state.set("name", newName)
        }
    

?

Just change the state. If you want to prevent errors, freeze and seal your
state. If you want time-travelling debugging, copy it via serialization. No
need to slow down production code by copying and throwing away data all the
time.

5\. bad: using Immutable.js, thereby slowing your code down by two or three
orders of magnitude. And I guess losing many GC and JIT optimizations by
throwing out different types of objects and pressing everything into the
generic map-structures, that immutable.js uses internally)

I wrote multiple sites/apps using immutable and redux, but i'm back to pure
react and lodash. Code is more concise, simpler and way faster, even though
componentDidUpdate has to use lodash's __.isEqual_ instead of a simple
comparison.

~~~
talldan
Unless something has changed very recently, you don't have to use Immutable.js
and redux together. Redux advises against mutating your state, but doesn't
prescribe a way to do that.

~~~
cel1ne
True, I did applications with redux and without immutable too, even wrote my
own syntax magic for that: [https://www.npmjs.com/package/babel-plugin-check-
data-access](https://www.npmjs.com/package/babel-plugin-check-data-access)

What redux misses as well are derived/computed properties, I think mobX has
them: [https://mobxjs.github.io/mobx/](https://mobxjs.github.io/mobx/)

~~~
acemarke
The standard approach is to store the minimal amount of data directly in the
state, and derive data as needed externally to the store. This is usually done
with "selector functions". See
[http://redux.js.org/docs/recipes/ComputingDerivedData.html](http://redux.js.org/docs/recipes/ComputingDerivedData.html)
, [https://medium.com/@adamrackis/querying-a-redux-
store-37db8c...](https://medium.com/@adamrackis/querying-a-redux-
store-37db8c7f3b0f) , and
[http://redux.js.org/docs/faq/CodeStructure.html#structure-
fi...](http://redux.js.org/docs/faq/CodeStructure.html#structure-file-
structure) for more information.

------
jpolitz
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
GopherJS [3] go through staggering amounts of overhead and effort to get
pausable, resumable, and stoppable computation in the browser environment.

I'm excited to see how this develops in React with fiber. It's much more
application-specific, but it's the same underlying problem.

[0] [https://facebook.github.io/react/contributing/codebase-
overv...](https://facebook.github.io/react/contributing/codebase-
overview.html#fiber-reconciler)

[1] [https://github.com/plasma-umass/doppio](https://github.com/plasma-
umass/doppio)

[2] [https://cs.brown.edu/~sk/Publications/Papers/Published/yk-
wh...](https://cs.brown.edu/~sk/Publications/Papers/Published/yk-whalesong-
racket-browser/paper.pdf)

[3]
[https://github.com/gopherjs/gopherjs#goroutines](https://github.com/gopherjs/gopherjs#goroutines)

~~~
ilostmykeys
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.

~~~
jpolitz
The main difference is that all of the use cases I mentioned necessarily don't
distinguish between calls/functions that may pause, and calls that don't (it's
just the semantics of those languages that arbitrary calls might need to
pause). So to use generators as a compilation target, _every_ function has to
be a generator, and every call a generator instantiation followed by yield*.

I actually don't know if that qualifies as "staggering". Your sibling comment
has some truth; I can only speak generally about Gopherjs and Doppio, because
I know them less intimately, but I know that Pyret and Whalesong were
definitely started before generators had widespread adoption. Compiling to
generators, rather than to the handwritten stack unwinding we have, is on my
list of things to try and measure.

Maybe "significant" overhead would be more obviously true than "staggering,"
since I don't have clear numbers to back it up.

Does that make sense?

~~~
ilostmykeys
<<So to use generators as a compilation target, every function has to be a
generator>>

Sure, but in those cases the functions are transpiled to JS so it doesn't
really matter, not like they're coded by hand, right...? I think I understand
what you mean.

~~~
jpolitz
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 function call returns, if it might
pause.

The first question for generators is if they are less overhead than this
manual process. There's a bunch of other details, too, but this is the main
one. And generators are certainly going to cause _some_ overhead over regular
calls and returns, just like the manual checking of return values has
overhead.

My original comment was about the lack of something like delimited
continuations in JS, which would allow saving portions of the stack while
intentionally minimizing the overhead of regular function calls. That's a
well-fitting language-level solution to this issue.

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

~~~
jpolitz
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](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.)

~~~
ilostmykeys
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... :)

~~~
jpolitz
> 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](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.

------
garrincha
I'm a Rails developer. I work on pretty standard web apps for a living. Some
more complicated than others, but still, web apps.

I still haven't found a person that was able to give me a concrete reason why
someone like me should invest time and resources into learning React and using
it in my projects.

This is an honest question, I'm not trying to be sarcastic. It's just that
there are so many frameworks that come out that it's very hard to know where
to invest your time.

~~~
dcwca
When you separate the web app from the server and build it as a stand-alone
React codebase, you can create applications that run in the browser, iOS,
Android (via Cordova / reapp) and cross-platform desktop operating systems
(via Electron).

Now wait a minute, you're going to say, I can do all of that with my server-
side web application. And faster too. Sure, but does your application work
when the user is offline? Does it have access to the client runtime's local
storage for data persistence? Can it access client hardware like the camera,
GPS, and accelerometer? Can it be distributed through Google Play and the
Apple App Store? Can it be installed as a desktop application?

~~~
kimshibal
He's a Rails app developer. Not a mobile app developer.

------
dustingetz
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 technical reasons that this can't be made
to work with dynamic scope? I think you lose a lot of stuff by, well, not
being actual function composition, only pretending to be.

    
    
        cljs.user=> (ns foo.core (:require [reagent.core :as reagent]))
        nil
        foo.core=> (def ^:dynamic *foo* 42)
        #'foo.core/*foo*
        foo.core=> (defn inner [] [:div *foo*])
        #'foo.core/inner
    
        ; React component call - broken
        foo.core=> (defn root1 [] (binding [*foo* (inc *foo*)] [inner]))    
        #'foo.core/root1
        foo.core=> (reagent/render-to-string [root1])
        "<div ...>42</div>"
    
        ; Direct function call - works
        foo.core=> (defn root2 [] (binding [*foo* (inc *foo*)] (inner)))    
        #'foo.core/root2
        foo.core=> (reagent/render-to-string [root2])
        "<div ...>43</div>"
    

A quick example of dynamic scope:

    
    
        cljs.user=> (def ^:dynamic *foo* 42)
        #'cljs.user/*foo*
        cljs.user=> (defn negate [x] (- x))
        #'cljs.user/negate
        cljs.user=> (binding [*foo* (inc *foo*)] (negate *foo*))
        -43

~~~
spicyj
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.

~~~
dustingetz
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.

~~~
spicyj
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.

~~~
dustingetz
> but that is simply not how React works ... React calls into the children

That's not how function components work, and i can easily imagine how to get
lifecycle methods back (including optimizations) on function components

~~~
spicyj
Please open an issue with more details about the changes you'd like and tag me
in it.

------
amelius
I'm wondering about something. Let's say I have a listbox with N elements, and
one element is appended or changes state. Will the reconciler have to perform
O(N) work for these operations?

If elements are added one by one, will the reconciler have performed O(N^2)
operations by the time the last element was added?

~~~
spicyj
1\. If a component changes state and its parent doesn't, then the other
children aren't considered so it would be O(1). If the parent rerenders, then
yes, we would consider each child and it would take O(n).

2\. Yes, it would. You would really have to go out of your way to make that
happen, though. React has a feature for batching state updates together to
prevent unnecessary rerenders if all of the updates occur together. Beyond
that, it's rarely an issue.

If you have a particularly long list you can use a tool like react-virtualized
([https://github.com/bvaughn/react-
virtualized](https://github.com/bvaughn/react-virtualized)) so that only the
onscreen rows are rendered at all.

~~~
freditup
> You would really have to go out of your way to make that happen, though.
> React has a feature for batching state updates together to prevent
> unnecessary rerenders if all of the updates occur together.

These batched state updates only happen "within React's walls", right?

So for example, say you had an RxJs observable that omitted a list of items to
display. When you first subscribe, it emits say, ten items, one by one, but
all in the same browser tick. It may emit items later in the future as well.

In this case, each of the initial emitted items triggers a full rerender and
you get the O(N^2) behavior, correct? My point here isn't that this is the end
of the world, but just that it isn't that crazy to say that it might happen.

~~~
spicyj
Yes, you're generally correct. If you can call into
ReactDOM.unstable_batchedUpdates at the top of the stack, that's another
approach. We might be able to make that the default in the future so
synchronous calls are always grouped even if React isn't on the stack.

We generally see the most success when you update your data wholesale with the
latest copy from your stores, but your RxJS example is a good one.

------
tambourine_man
Building React From Scratch:

[https://www.youtube.com/watch?v=_MAD4Oly9yg](https://www.youtube.com/watch?v=_MAD4Oly9yg)

------
Ryan_Shmotkin
Also: ReactJS: Under The Hood — ReactNext 2016

[https://www.youtube.com/watch?v=xsKYAa1ZXpQ](https://www.youtube.com/watch?v=xsKYAa1ZXpQ)

------
mrcactu5
this is really excellent

I am part of a meet-up of theorists who do build websites... but are also
curious how frameworks like React work under the hood. What algorithms they
use, frameworks, etc.

It's possible to build a nice web-site without knowing any code at all. Or
knowing some code and algorirthms. In that case the code may become very
difficult to control, or revise or share with your peers.

If you know some algorithms and code it's sometimes helpful to see what is
under the hood.

------
Wintamute
Awesome that these implementation notes are so simple and laid out over just a
few pages. Speaks to the conceptually simple underlying model of React I
think. Nice job.

------
caub
jsx makes it look a bit magical, learning React with const
v=React.createElement helps also. Ex: [https://github.com/caub/todo-
list](https://github.com/caub/todo-list)

------
joshmarinacci
I really appreciate getting an inside look at complex code like React.

------
andrewvijay
Brilliant work. Explanations like these are true gems. Kudos!

