Hacker News new | comments | ask | show | jobs | submit login

The author does not understand React :(

> React by itself doesn’t actually solve how to propagate changes

It does actually - you update the state, then React propogates the changes for you through it's props mechanism. Flux is an extra layer of indirection over state changes if you need it: https://twitter.com/floydophone/status/649786438330945536 (edit: I regret my tone here, there is clearly ongoing work in this area and no widely accepted best practice yet)

Flux is not message passing, React components do not redraw themselves, React components do not pass messages to each other, Flux only superficially looks like winapi because of the switch statement in that particular example.

React provides the view as a function of state. winapi is nothing like that.

React is a giant step towards functional programming. winapi is definitely nothing like that.

edit: Windows -> winapi

>The author does not understand React :(

The author understands React quite well, it's this comment that, while also understanding React, misses the author's point by insisting on the superficial differences.

>It does actually - you update the state, then React propogates the changes for you through it's props mechanism.

That it achieves something (change propagation) it doesn't mean it's the solution to that thing. There's a reason the author used the wording "doesn't solve" (instead of "doesn't handle", "doesn't allow" etc). Flux (and it's variations) is what really solves propagation in React without it becoming a tangled mess.

>Flux is not message passing

Nobody said it is.

>React components do not redraw themselves

They do, that's the whole point. React components manage, compose and return their DOM representation structure which is the HTML analogous to "drawing". return(<b>{msg}</b>) IS drawing.

The fact that they don't have low level rendering code embedded in them is just an implementation detail (because of course the DOM already exists).

>React provides the view as a function of state. Windows is nothing like that.

Only the author doesn't mention Windows, the abstract OS, he mentions a specific implementation of its UI engine that clearly provides the view as a function of state.

Similar miscomprehensions of the article drive the other objections.

I don't agree with your core thesis, "redraw" and "draw" are imperative verbs which are ubiquitus in winapi, `return <b>{msg}</b>` is not a verb but a value, this not superficial or an implementation detail, but rather a massive change in programming model to the point that IMO comparisons that overlook this difference are not useful at best and harmful at worst

That's because HTML has an abstract declarative way to define the layout/drawing whereas Windows did not.

But that's not especially important when it comes to React in general. The important is that the "how to draw myself" (in a high level) never leaves the view, not whether the "drawing instuctions" happen inside the app (as low level draw commands) or outside (by a mechanism that handles composable values representing markup).

In both cases the view is a black box -- in that nobody else determines it's layout/style/contents. Whether somebody else or the view itself draws them might reflect a functional vs more imperative style, but it's not the key benefit of react.

In fact there are React implementations like React-Canvas that don't use the DOM/diffing mechanism at all -- they just invalidate the whole top level "viewport".

Why? Where is the practical difference for the program design between "returning html that the browser then draws" to "drawing themself" at the end of the flow?

Serious question, and I'm not a Windows programmer, I might be missing something.

There is no practical difference. You could argue that manipulating the DOM is the closest analogy to Draw(), but trying to make a distinction between that and generating HTML (that is going to be used to update the DOM) seems like splitting hairs.

And: It's true that DOM manipulation could be seen as "lower level" than HTML generation, but the main difference for programmers is the API. A framework that forced you to call createNode('b') and createTextNode() and appendChild() would not get the adoption that one which lets you say "return '<b>Foo</b>', all other things being equal.

Components drawing themselves is a side effect, and components returning markup are pure functions of their state.

One is a lot easier to unit test than the other, for example.

your question resolves to when to choose imperative vs OO v functional, and while I'm not going to try to discuss that here, they are profoundly different. Winapi is imperative, React is functional.

>Winapi is imperative, React is functional.

That's a red herring. That would be relevant if we were discussing the imperative vs functional methodology.

But what the article discusses is the similarity in the architecture of two view management systems.

And even more so, the imperative changes in the Windows scheme described are encapsulated inside the views, and the overall system is more functional that you give it credit for. But, again, I think that this is beside the point.

I call forceUpdate to trigger redraws when view state is managed outside of props. Feels like an imperative redraw to me.


Well, yeah. That isn't the recommended way to use react.

Its pretty essential for non-trivial applications with data-structures more complex than can be handed over to React. setState is just sugar - it also calls forceUpdate.

"It does actually - you update the state, then React propagates the changes for you through it's props mechanism."

That's not all that different from what DispatchMessage does in the Win32 API.

Anyway, I think that what FRP, React, and Win32 all have in common is a reliance on explicit local state. Components are not supposed to maintain references to other components and send them messages directly; instead, components are supposed to pass messages back to the top-level event loop of the component hierarchy, which then dispatches them to the appropriate subcomponent, which is supposed to take the appropriate action based only on information in the message and its own local state.

This is in contrast with later OOP frameworks like Swing, MFC, NextStep/Cocoa, or the DOM, where it's common to hold a direct reference to another component and then update it via observer. These hold implicit global mutable state; the state of distant components in the tree may be silently updated in response to an event firing in some other component.

The trade-off here is whether you want spaghetti code or ravioli code. Functional frameworks (FRP, React, Win32) tend to locate logic for the details of what should change outside of the components themselves, which focus only on how it should change. That keeps component logic simple and prevents unexpected side-effects, but means that the code for the controller itself can become ginormous. OOP frameworks tend to move logic for what should change into observers, which then reach across the component tree and update a number of different components directly. You break up the logic into a number of bite-sized pieces, but there's no way to see or predict what the effect on the system of a whole of an event will be.

Interestingly, Polymer/webcomponents goes back to OOP-style ravioli code. I suspect that like the rest of the computing world, we'll see a see-saw effect on a roughly 4-5 year cycle, so maybe around 2018 observers and mutable state will become popular again.

The author does not state that React components are literally redrawn like Windows views. He simply recognizes a pattern and makes a pretty apt comparison.

> React by itself doesn’t actually solve how to propagate changes

In my experience, it really doesn't solve the problem all that well for a sufficiently complex app. I'm willing to give the author the benefit of the doubt on that point.

> React is a giant step towards functional programming. Windows is definitely nothing like that.

Err, that's not entirely true for MVVM apps, where views are just declarative markup that are rendered (retained mode) based on logical state provided by data bindings. It's been like that ever since XAML was introduced in 2006.

MVVM makes ubiquitous use of mutable state which means model change listeners, callbacks calling callbacks etc, FP/React is about using immutable state to dodge all these problems by design.


The point is that in both React and XAML apps, you (usually) don't write code to mutate the View. In your code, the View is just a declarative construct.

There's nothing stopping you from making immutable ViewModels in MVVM, other than bad perf (which React would presumably also suffer from on larger apps).

The point is React is not just View, each component is a view plus the part of the "controller"/"presenter" that would take care of populating the view. And the controller/presenter is where state is always encapsulated, which React gets (mostly) rid off.

Facebook doesn't seem to perform so badly in terms of a larger app. More varied components on a page than most applications in general. Most of the quirkiness I see in FB tends to come from how they deal with eventual consistency with their backend in order to scale to millions of simultaneous users.

Most applications don't have the latter problem.

I bet they do a lot of shouldComponentUpdate, which kills the functional purity.

XAML control properties do the equivalent of shouldComponentUpdate by default. Same ideas, just doing diffs in different places.

> I bet they do a lot of shouldComponentUpdate, which kills the functional purity.

functional purity enables shouldComponentUpdate

Adding a late comment to appreciate the MVVM synopsis in your blog post. The pain of tracking lots of callbacks and cascading updates is a lot of what Pete Hunt was talking about in Be Predictable, Not Correct [0], but there's not a lot of discussion applying that analysis to MVVM as for other forms of MV*. I find it's a real concern in WPF apps with much of any complexity.

[0] https://www.youtube.com/watch?v=h3KksH8gfcQ

Not sure I fully follow this.. I understand component's don't pass messages to each other, don't redraw themselves. But isn't flux basically doing somewhat of a uni-directional message passing? Maybe my terminology is incorrect and i'm mixing up concepts , but it feels like a modified pub-sub type architecture (difference is that when something is dispatched its sent to _every_ registered callback as opposed to subscribing specifically). Still feels like a form of message passing. I really don't know enough about how old windows development worked, but from this article it seems like it's a _similar_ type of pattern.

One nitpick I have though is the giant switch statement they mention. I typically put all this into an object so you don't need to traverse through some huge switch. Not sure why everyone just doesn't do something like -

  AppDispatcher.register(function(action) {  

I wonder if you are running into the same thing that I have seen with React. Because each node in the React tree is called a "component", people tend to think that it should be self contained. In the article, the author opines that the state should be kept in the same object as the view.

I think this is where people run into problems with React and it is what gets them running for something like Flux early on (I agree that it is not necessary in many cases).

If instead of putting the state for a node in the same node that renders it, you put it in the parent node, suddenly things get a whole lot simpler. To look at it the other way around, a node keeps the state of its children and passes that state as props to the children. That way the children are all idempotent with respect to rendering.

So what do you do when the child wants to update the state? The parent passes a callback to the child and the child calls that callback. This updates the state in the parent and passes new props to the child.

The effect of this is separation of rendering logic from update logic. Every React component knows how to render itself from props. It knows how to request an update to itself with callbacks that are passed as props, but it doesn't know how to update itself. The parent holds the state of the children, but doesn't know how to render them. It simply passes that state to the children. The parent knows how to update the state of the children.

You may recognize this pattern. The parent is the controller for the child. The state that the parent holds is the model for the child. The parent node together with its children form an MVC structure. Of course each child can be a parent too, which leads to a hierarchical MVC structure.

With this, you will usually not need Flux. Of course, one could imagine that passing callbacks is tedious. We could create a special object that handles all the "actions" that the child might make. Then we could simply pass that object in the props. Sometimes, the state that you want to update is several layers up and you end up passing this object down the tree just so that the very bottom node can call "actions" on it. So, instead, we could make this a singleton object and call it a "dispatcher".

Also, you sometimes have situations where, for convenience sake, you might want to store the state in several places in the tree. Let's say every terminal node needs a certain piece of state, so you end up having to put it in the top node in the tree. Then when it changes, you end up rendering the whole tree. So instead, you can create an object (let's call it a "store") that holds the state. Individual nodes observe the "store" and when it changes, they pass the relevant values as props to their children (never updating their own state or rendering it directly!!!). We can even make this "store" get updated by pushes form outside events.

And there you have Flux. Useful in certain circumstances, but I would suggest that if you are finding that you absolutely need it, it's because you are trying to render local state, which you should not do.

With respect to the original article, this is what Windows got wrong (with everyone else, of course). The code that renders the state, holds that state, which makes you reach for WndProc().

I've watched a lot of talks and read a lot of articles about these technologies, and this is the clearest progression I've seen explaining the rationale for Flux.

And thank you for making the point about what Windows got wrong. It is a critical distinction between two things that otherwise look very, very similar.

Can you write another one on the progression to Relay? GraphQL is where they lost me.

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