Using pure functions to compose a UI along with keeping all the data in one place have been a huge win for our project for a few reasons.
1. All it takes to debug what a handler is doing is what effect it has on the DB. Our handler functions are also mostly pure functions which take the current-db as a parameter.
2. We can serialize the state to local storage (or a string) and reload it from local storage (or a string).
3. Using the repl, we can investigate the state of the application while it's running.
If it doesn't take hold this generation, it will definitely cycle back decades from now as the dominant way to structure ui code.
Between unidirectional flows and observables (or maybe some other async primitive) UI is getting very interesting these days. Personally I prefer rx to core.async. Core async just looks too awkward and low-level for me. RX I got immediately - a promise for a stream. Great stuff.
Thanks for any info.
It looked for me that way even before I started learning about React.
If you want to "edit" the previous screen in the stack you just build a new version of the screen you want to "edit" then remove old one from the UINavigationView.viewControllers array and insert the new one. Then popViewControllerAnimated() back to it. It works very well.
Making iOS apps this way has been a dream come true for me.
i havent seen that "build a new version of the previous screen" tho, at least not the way you describe -- most of the time you just rely on `viewWillAppear` calling `renderView:model`, and `viewWillAppear` gets called on `popViewController` by default.
Does this approach scale well ? Have you done large apps ?
FB has an open source ComponentKit Framework.
Sure, for every piece of the UI that commits a change to the model, such as a save button, you need a callback to pass the updated model to.
You pass a "movie" object and a "saveMovie()" callback into a screen that allows you to edit the details of a movie. You edit the fields then click "save". The code that runs when the "save" button is clicked builds a new movie object then masses the new movie object to the the callback, like this "saveMovie(editedMovie)". The "saveMovie()" callback makes the API calls, database calls, etc, and the movie editing screen doesn't need to to anything else after that, and inside "saveMovie()" you can push a new screen, pop back to the previous one, or whatever. Does that make sense?
"Does this approach scale well?"
I don't see how it could be much worse than making API calls from UIViewControllers. If you do this, you're instantiating new UIViewControllers more often, so if you're loading a bunch of images on a certain view you may want to hang on to a reference to that particular view instead of building a new one each time. But, in my experience, the code ends up being a LOT cleaner than if you treat your UIViewControllers as mutable objects. So from an architecture and maintainability standpoint, it scales really well.
At work we're at 10k loc which isn't huge but it's fairly large for a clojurescript app. Middleware on event handlers is a great abstraction. It's only been a few weeks since the refactor and there are only three of us on the team but I'm not foreseeing any scaling problems. The only changes we made are about 20 middlewares and namespacing our event keywords. We experimented with a forked version of the framework and running multiple re-frame apps nested inside each other but it wound up not being worth the effort.
The only scaling hazard I know of is that re-frame has an internal global atom so that might cause a problem if you're mixing multiple re-frame apps on a single page but it doesn't affect us and reworking the framework to not use a global isn't too difficult.
In that moment, I was genuinely taken aback to realise that by using clojurescript and reagent, I was operating in what some regarded as the future. I knew it already, of course, but that moment pushed it home. Nice!
re-frame is pretty handy too. (But I'm the author, so on that point I'm pretty biased).
Are you planning to incorporate datascript or datomic at all in your projects?
Putting blinking things in the periphery of vision is a very well known pattern when you explicitly want to draw people's attention. If this were email notifications, blink away. But it's actively drawing the reader away from the core purpose of the site--reading the damn article--without adding anything of value.
"Oh, other people are coming here? Social validation! I'm reading the right things! Now what was I reading?" Complete malarky.
Seems to hold up pretty well.
I agree though, it can be a little distracting, but it's out of sight with a little bit of scrolling.
While I'd be a fan of the feature on a desktop this is the kind of thing that makes me want to see uMatrix work on Android.
These features are never implemented as opt-in and the only way to opt-out is with something like uMatrix.
I tried to read the article for 15 seconds but the highlight just made me close the whole thing.
I think the biggest difference is that XAML was really terrible to work with in part because of proprietary components and the massive tooling around it. I remember I'd have to use Blend whenever I wanted to change how something looked. And then there wasn't a great application structure that moved along with it. I pushed really hard for us to switch from code-behind to MVVM for our application architecture. It worked great, and made everything much easier, though it was really abstract and styling was difficult still. But that was my first job--maybe if I went back it would seem trivial.
I don't know if this applies to XAML, but once you start thinking of React in a component architecture model (knowing JS helps too!), the declarative style makes so much sense.
This is a long shot, since most people have a love/hate relationship with the VisualStateManager, but I have been trying to build a declarative visual state thing over at https://github.com/jgable/react-vsm. It's not done, only messed around over a couple weekends, but the gist is there.
Sure, the UI is pretty much pure, but it moves all the state who-did-what-in-what-order mess to the View Model, so the problem is just moved, not fixed.
I think that working at such a high level will enable an unparalleled dynamic between designer and developer.
My dream job right now is to be able to sit down with a designer on one side, and a (client / domain expert / stakeholder) on the other, and iterate on an interface in a very tight feedback loop.
I feel like Elm has really inspired me in a positive way, and is helping me to recover. I'm already feeling pretty endeared to it's community, and want to give back as much as possible, as soon as possible.
If you haven't checked it out yet, I highly recommend it.
I have been putting a lot of work into devcards lately so forgive me for being so focused on it.
1. Create pure UI component
2. Describe allowed state space as DSL/type - https://news.ycombinator.com/item?id=8963000
3. Plug in fuzzer
4. Watch till you catch an undesigned state
5. Fix & repeat
- expand it in physical space (as in article) to see main variants
- fuzz it (= expand/project randomly onto time axis) to catch errors visually
> the definition of an application’s UI as a pure function of application state
I presume "application state" means the stuff that the app cares about, and not the stuff that it doesn't.
But the app's actual, rendered UI incorporates state from multiple sources. For example, if you have a text field, it has "application state" like its positioning, "framework state" like where the text selection(s) are, "user state" like what size the user dragged it to, etc. And your "pure function of application state" necessarily discards this non-application state.
So then how does this state ever get preserved? The answer must lie in the "diffing algorithm:" the thing that determines what has changed, and attempts to reuse as much of the old UI as possible. A good algorithm will reuse an element and preserve its state. A naive algorithm will drop state and result in bad UI.
So in this model, aren't you taking a huge and hidden dependency on this diffing algorithm? What's the right way to ensure continuity of non-application state across updates?
Yeah I was wondering about some of this stuff as well. I don't understand how you could hack UIKit to work this way.
Do you think this is really all that superior to using KVO and bindings (We don't have bindings on iOS but still .. ) ?
Also animations aren't easy to do this way.
I think we can follow some of the guidelines they have here :
And reap most of the advantages.
As for optimizations ,
He mentions immutability , we already have that using Swift structs.
Then there's the differ
The diffing algorithm shouldn't be a one size fits all super algorithm but a collection of dumb components you can put together to get the perfect diffing behaviour for your particular use case.
The differ should also animate the state transitions appropriately where necessary.
Then we have this :
Though I haven't had the time to look into it. It has to be good since FB claims they rewrote their whole app in it.
Using your video player example, if the video is loading (state 1), and once loaded, it starts playing (state 2), wouldn't a pure functional approach imply the entire <video> DOM element is replaced by a new one in the change from state 1 to state 2? What if I only wanted to animate the loading bar away and fade out the thumbnail when leaving the loading state, while maintaining the original HTML element?
I'm curious to know if you've thought about this, and have any insight, because it's something I hope to understand. Thanks.
Is the approach inherently flawed for fast UI applications?
I feel like this is a much more semantic interpretation of frameworks than trying to compare the likes of Angular, React, or JsBlocks on speed, data-binding, and whether they support x,y,z use cases. Kudos rauchg.
Building out my UI with Meteor and React was so cool, I found myself having fun again.
The integration isn't totally settled yet, but a lot of work is being put into it at the moment and Meteor is a perfect companion to React, much simpler than implementing your own Flux or Relay backend.
For further info, you can also look here:
I'm thinking it could be interesting to combine some agile technique like ATDD and user story mapping with such a disciplined formalism.
I started with just using contentEditable but you have even more exciting cross browser issues, the output is both inconsistent between browsers and terrible, and you can't do controlled input because updating the editable content DOM at all breaks undo.
With the virtual DOM model, updates are roughly O(n) where n is the size of the virtual DOM. The way to handle very large amounts of input is to window the input data and only vdom render/diff the subset of the data that's currently visible.
I only ask because I determined it impossible to use React for a RTE over the last year and instead opted to use Redactor/contenteditable. I just could not solve those problems easily/quickly enough to make it worth it :(
I'd be extremely curious to see any at all type of implementation for a RTE in React if you have one you could share.
For rendering, I had a component that rendered the block element by splitting the text string at the indicies specified by the text ranges, inserting the appropriate tags, and dangerouslySetInnerHTMLing the text. I'd probably just manually generate vDOM nodes if I were to revisit it.
I didn't do line breaking, moving up/down was done using the caretPositionFromPoint code: create range, get it's bounding box to get initial x/y, getClientRects to get the position for the next line, caretPositionFromPoint, get block text index.
In this case (I did mention it was basic) I wound up not having to do cross-block operations because the UI never had more than one. I had a plan but not having to do it makes me happy.
In conclusion, you're probably better off just using an existing RTE.
What do you y'all think of something like this?
Giving up efficiency in the slow part (DOM updates) in exchange for efficiency in the fast part (determining what changed) is backwards.
Why would you ever want to read from the DOM in a well-architected app? Unless you are building a component that can be "dropped anywhere", you control what the DOM shows.
If you need to read/write to the DOM, you can cause layout thrashing and seriously slow down your UI. You might have to resort to using a library like FastDOM to fix it.
I think you are overstating the "dangers" of having the UI be a pure function of the state.
As for when you'd want to read from the DOM in a well architected app, the easy example is drag and drop. Unless your drop targets have fixed sizes or your needs are simple enough to purely get away with mouseenter/mouseleave events then you need to query the DOM for node sizes. Other situations I've run into are text breaking and rich text editing.
So what? This is orthogonal to the problem of rendering the basic UI as a pure function of the state.
You can store the state of of components inside components, there are many ways you can do it. React does it. We've implemented it in our framework: http://platform.qbix.com/guide/tools
As for how you would remove event listeners when a component is removed, that needs be coded of course. It would be nice for the developer not to have to write all that repetitive code. Of course, that also means you'll have to know when a node is removed from the DOM, and in browsers that don't support mutation listeners you'd be hard pressed to know that unless the user uses YOUR standard methods to control the DOM. All around it's a better solution (if you control the development of the app) to architect the app so that the model goes through YOUR framework to update the view.
-each @sorted, ->
2 - http://haml.info
Of course, the ecosystem will build better tooling to support less technical folks, but for now it's still heavily engineering-focused.
View counters have been dead for a long time and need to stay that way. No one cares how many views you have and certainly no one wants to have a flashing box at the top of the page they're trying to read.