Hacker News new | past | comments | ask | show | jobs | submit login
Simpler UI Reasoning with Unidirectional Dataflow and Immutable Data (omniscientjs.github.io)
93 points by audionerd on June 7, 2015 | hide | past | favorite | 21 comments



I'm glad to see more people writing about functional programming for JavaScript web applications. I liked a lot of the ideas here, but I don't really see the need for the "component" data type. Why not just regular functions? Also, I don't think that letting each component determine whether or not it needs to redraw is a good idea. I'd rather just let the rendering system handle it by diffing the tree. With immutable data, it's trivial to see if a node has changed and redraw the subtree.

I've been doing experiments with Mithril (provides virtual DOM) and various functional reactive programming libraries (such as Kefir), and I think something like that is the right way to go.


I'm not quite understanding how the component determines whether it should redraw itself in this example. The component gets redrawn when the render function is called (if the data it's passed is different and if the output of the render changes) and the render function gets called when a "swap" event is raised, which is raised when a component updates the app state, but it's not saying "please redraw me now". It's not aware it's getting redrawn, it just happens.

Unless I've mis defined what "letting the component determine it needs to redraw" means in this context.


Component here is just to illustrate that the type is of a view component, and due to it being implemented on top of React. `component()` is a factory/syntactic sugar on top of React allowing you to have referentially transparent components (as you get arguments passed to the render function).

In time, React hopefully (and probably) gets support for stateless functions without the syntactic sugar (https://github.com/reactjs/react-future/blob/master/01%20-%2...). This will be supported when React adds support for functions returning VDOM elements.


I evaluated this, and wrote some code in it, but ended up choosing nuclear-js as my react framework. Has many of the same concepts, but within the flux paradigm, and has the awesome concept of getters. Getters are a combination of several keypaths into the immutable app state, plus an optional transform function.

Also, nuclear seems quite stable at this point, and the GitHub issues are about pretty practical stuff, while omniscient has had very recent breaking API changes and many of the issues are discussions about functional purity etc. Just my two cents on why I chose one over the other.


This topic is also covered in the talk from JSConf Budapest: "Functional UI and Unidirectional Dataflow": https://www.youtube.com/watch?v=JNMWi7Z0Ssg


I really like the Om/Omniscient model of using immutable data to represent application state - deciding whether or not to rerender a component and all of its subcomponents only requires comparing a hash.

However, for the app I'm working on, I want most the single source of truth for most application state to be on the server. I'm passing data from server to client in JSON. Has anybody come up with a good way to serialize/deserialize immutable objects? I might try out transit-js, but I'm wondering if anybody has already gotten this working?


I'm using Fluxible [0], with immutable.js [1], and I just create my stores using createImmutableStore from fluxible-immutable-utils [2]. Since you're keeping the state for each store in context._state it'll just call context._state.toJS() when you're dehydrating, and Immutable.fromJS(payload) when rehydrating.

[0] http://fluxible.io/ [1] http://facebook.github.io/immutable-js/ [2] https://github.com/yahoo/fluxible-immutable-utils/


Thanks, fluxible looks like it might be the best isomorphic solution available right now, and the fromJS() and toJS() functions are what I would need.

I guess I was initially hoping to use immutable data for the performance benefit; being able to implement a fast shouldComponentUpdate() with a simple equality check.

But I wonder how toJS() and fromJS() performance-wise... if they have to create new objects by deeply copying the objects, maybe that would be just as bad as doing no shouldComponentUpdate optimization.

I guess I'll just stick to the "premature optimization is the root of all evil" concept, and try to get something working with carefully mutable data first.


Oh, no no. That toJS() and fromJS() only happens on the initial load, it's pretty fast. Using immutable.js you can get way better performance and you don't have to worry about data ever changing on you unexpectedly.


A bit of a tangent:

I have been experimenting with replacing REST style backend interaction with websockets an having the state manipulation round-tripping through the server.

Instead of setting values on mutable objects and telling the server about it postfact (having to deal with errors like ba validation and connectivity somehow), I would send a change command to the server, which would decide what to do, and push an updated state back. Once I detect this change of state, I update the UI.

This simplifies error handling a lot, since the user simply can't update state if an error occurs. And I get cuncurrent multi user editing for free.

Unless you want offline editing (which is hard), it's a big win.


I'm not sure if this is good API design, but it definitely sounds like a fun thing to try. I'll keep it in mind for a personal project.

Edit: Actually when you pass all state as props, this will even apply to typing in a textarea. Better make sure the roundtrip is pretty short!


I let the textarea take care o itself. I hate when they try to change the text while I type.

The update wouldn't be sent until it is unfocused or possibly after a set delay after the last keypress.


That introduces a noticeable delay. It's acceptable in some situations but in others less so. Having immediate feedback on button press or text entry makes the UX much more pleasant.


This is still rather easy. You can change the UI (i.e. set the button into a "Loading..." state) directly after the click and when the server sends back the response act accordingly. An error would reset the button and prompt an error message.


Exactly. The difference is, this becomes the default behavior (minus the spinner), and simple to implement.

With rest, it would be a much more manually implemented feature. The default would be to just fail silently and potentially drop important data on the next page load.


Unidirectional dataflow is nice, but how do you figure out what parts need updating? And is all the bookkeeping necessary to figure that out not expensive?


You make it explicit in the system. I don't use React, but I do dabble with functional reactive programming. In an FRP system, you define a directed acyclic graph of values, where the edges are dependencies from one value to another. So, when an event enters the system at one of the graph's roots, only the things that depend on it will be updated.

Furthermore, you can avoid unnecessary re-computation via the usual techniques like memoization. One of the big advantages of the "virtual DOM" is that we get to build documents using a real programming language, not some HTML templating system.


Explicitly building a dependency graph is cumbersome. I'd rather see my programming language (compiler) building it implicitly as the program runs.


What? It's no different than saying which inputs are passed to a function. Also, languages with macros can add some nice syntactical sugar on top. JavaScript, of course, doesn't have a macro system so there's not much we can do there in that regard.


> It's no different than saying which inputs are passed to a function.

In that case, why isn't it just (more or less) equal in syntax to a function call?

Why do I need to build a graph, where a functional language would just let me build it implicitly.


Just keep updating everything on every input event! Sounds crazy but with virtual DOM it should be pretty doable at least for prototypes.

https://github.com/unframework/vdom-live




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: