I've noticed the f(data) = view paradigm dominate the clojurescript landscape ever since om and reagent[1]. There's also a library that literally uses function calls and parameters for every piece of UI called re-frame[2] (which also has a badass readme).
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.
It is virtually impossible to go back to a non unidirectional ui without feeling gross and disgusted. Working on a major angular project after doing some cljs with react feels like a decades step back - back to a horrible jsf like two way data binding disaster. Where is my state? Where is my api? Why did x put y here?
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.
Clicked through to this, spent a bit of time going huh? But it very gradually started to come into focus. I definitely going to give it a whirl for a few small in house webapps - thanks!
After spending 3 years writing iOS interfaces solely in UIKit, my first work moving to cloud app development was solely in React/Om. It was like riding a bicycle for the first time. Amazing how far you could go in UI work with such (relatively) little effort.
You can totally do pure UIViewControllers with Swift. Just initialize them with all the data they need and give them callbacks (or "handlers" if you want to call them that) for every UI control. Keep a reference to your UINavigationController somewhere outside (in AppDelegate? Hopefully not, but you could :p) any of the UIViewControllers, and use pushViewController(screenYouWantToShow(dataToPassToScreen, callback1, callback2)) to move between views.
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.
having worked on a number of iOS apps (everywhere from 5k loc to 80k loc), i have literally never seen anyone work any other way -- you just put `[self renderView:model]` on your controllers and it's a pure function which sets up the state such that the system `layoutSubviews` call is also pure function.
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.
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.
I tried this approach with my current Swift project, but I found that it doesn't scale to a certain level of complexity. A ton of the UIKit APIs are naturally stateful, so you end up having to work around them in weird ways. Animation is particularly hard when your view controllers are pure. The solution I opted for was to instead create a unidirectional data flow and just "refresh" the state of the VC every time it gets passed different data. This way, you get most of the benefits of one-way data flow and the state of your VCs is (somewhat) pure, but you can easily drop down and use the UIKit APIs as needed.
I see what you mean. I'd probably run into issues if I was doing a lot of transitions animated transitions and interactions. With my method nothing is really stopping you from creating a VC that has a public refresh function though. It would just have to break the pattern of ask the others a little bit.
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.
The idea is to get away from callbacks altogether. That is the idea of FB React and Om/Reagent. What you describe is not at all the paradigm for UI development I'm talking about.
I understand that, I wasn't saying it was exactly the same. I do think that what I'm doing is better than what iOS tutorials usually teach you to do though.
Do you think it's a good idea to write React components with APIs somewhat mimic UIKit, such as UIView? Or is that possible? I'm new to web app. Thanks in advance.
Thanks for your advice. One of the reasons that make me think this way is it seems there are some attempts to pick up inline CSS. IME, I believe object-based (or thing-based, it does not have to be strictly related to one paradigm) system is easier to understand when I work on UI. I'm probably biased and lack of enough knowledge and experience to see it through. This is just my current guess.
OO is easier to grasp initially, but you have to keep track of lots of state, and that gets really hairy really quickly. The functional approach requires more forethought, but there's much less room for error because you aren't manually moving state around your application. It's a totally different way of thinking about UI, but it's worth it for the productivity you gain in the long run, in my opinion.
Yes, states always went messy when I made a visual module a bit complex (well, more than 50% was due to my unpolished skills though). The functional approach is better. My main mind-barrier as mentioned in the rely to hellofunk is not able to manipulate appearance through JS API. Perhaps I should stop thinking about it and just keep writing till I get used to it.
The React model of UI development is much much easier to work in than an OO model of UI. There are fewer wires between objects (often none at all), nearly zero state in each "object" or UI component (or none at all), no callbacks, just mostly one-way declarative setup. Apple should (and is) giving it some attention. Many former Apple UIKit developers as well as those working on things like ReactiveCocoa (which is a different paradigm) are enticed by what Facebook has done with the React paradigm.
I agree with you. I did not try ReactiveCocoa since I spent time on Swift instead. But I know the react way is way easier to manage the states all over the place. Any idea on the inline-CSS thing? Is operating directly on JS a better way to go? I guess I'm too used to doing things like UIView.alpha = 0.4 to manipulate the appearance of an visual object :) Thanks for being patient of answering my silly questions.
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).
Interesting, as I had the complete opposite reaction. I found it distracting and had to scroll it off the screen to be able to focus on the content of the article.
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.
You might want to work on improving your ability to focus. If you're that easily distracted, then you might need to work on mindfulness. It isn't like it's banging a gong every time the number changes.
I think that if somebody is talking about UI design, there shouldn't be an annoying, randomly highlighting yellow object taking my attention. It is really bad UI design. Who benefits from this information? Nobody else but the writer of the article.
I tried to read the article for 15 seconds but the highlight just made me close the whole thing.
Actually, there is. Contrary to what ninja rockstar hipster Wangular.js devs like to believe, not everyone runs an i7 on a gigabit fiber line. Little useless counters like that (who the hell cares how many people have viewed a page anyway? I sure as hell don't, and can surely survive without being bombarded with attention whoring) tend to add up. And even for those who do run an i7 and a gigabit fiber connection, it's invariably in response to little bullshit widgets like these clogging up CPU cores and network connections.
Sure, go ahead, downvote my comment while dismissing it with snide remarks. You're just contributing to the reasons why people disable Javascript or relegate it to on-demand / click-to-play functionality.
My first job was as a Silverlight developer. I used to complain all the time about XAML, and as a huge React fanboy, its been lurking in the back of my mind. Why would I like JSX but not XAML? Especially since early Silverlight projects used 'code-behind', which is essentially just a c# file that is paired with each component. How is this different than React? At all?
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.
I also came from a XAML background to React and had the same feeling.
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.
Same here, style in XAML is very verbose, and conditional rendering is not created from the code with ternary operator and conditions, but with templates in XAML that are very verbose and it is hard to remember how to use them. Also the use of 2 way databinding cause a lot of problems.
It's also the lack of support of CSS in XAML. JSX didn't re-invent HTML with proprietary tag names and strange layout attributes... though this whole CSS in JS thing I can start to get behind if the tooling improves.
It's also how Backbone has always worked.. Canonical Backbone has you `this.model.bind('change', this.render)` where `this.render` is a pure function (typically just passing `this.model` to a Handlebars template).
Silverlight's UI system was MVVM, based on AngularJS-like two-way data binding (actually, KnockoutJS is inspired by it).
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.
This is exactly the approach Elm takes. The recommended way to structure the application is so that the view is a pure function of the Model (application state). Moreover, the change to application state is also pure: there is a pure function that takes an event and an old model, and then returns the new model.
It's taking me a lot longer than expected to get productive in Elm (because of personal issues (burnout/depression)), but I'm convinced that the speed limit is going to be very high at that altitude.
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.
This ability to concretely display a component in all of its different states is one of main reasons I created devcards (ClojureScript, Figwheel). One can use it to create storyboards live as you are programming.
You don't need a library to do this stuff though. Just a functional approach and a willingness to work on a storyboard application, which iterates over all the states of your components, instead of just working on the main application.
Exactly but keep pages of these ui components and review them before a push to production. In fact, work on the component in this setting to get maximum feedback.
> 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?
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.
Thanks for the essay, it was a great read. Maybe my understanding is wrong, but I am slightly perplexed by one idea. I really like the link you have created between the designer's and developer's workflow, and I think your essay touches on an ideal. But, from an applied perspective, this may be difficult to attain when creating components that need to visually transition between state within the original element.
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.
React (and others, I'd assume) does this by keeping a virtual DOM and then only applying the DOM manipulation necessary to get existing state to match desired state.
Hey, sorry for the late reply. I have touched on React. But, I am familiar with Ember, Backbone, Vue and Riot. Nonetheless, could you please explain further?
I like the idea of having pure functions f(state)=view,
however, wouldnt this be very slow for very big ui applications? Also the only reason todo this on web is to use some sort of double-buffering and prevent redraws before everything is updated. On mobile native you have control over these things. But thinking about it, doing purely functional UI for native apps would never work, right? You need todo a dif each and every time the ui updates. And this happens a lot for example for sliders, or animations.
Is the approach inherently flawed for fast UI applications?
Not that I'm terribly familiar with the functional approach, although after reading some of this I will definitely be examining it more thoroughly and exploring its use in personal projects, but would memoization appease you?
>In general, comparing frameworks in terms of features seems inferior to examining the model it imposes on the programmer. The latter will inform you about how well the code will fare over time as the product matures and the team grows, but the former won’t.
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.
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.
I've done a basic RTE in React for a client. I've done three now and RTEs are always annoying but the difficulty in this case is largely in how you model it. I chose to create a JS object per block element (usually a paragraph) consisting of the text as a string and a set of text ranges (start/end/type). Input was via hidden text field that's updated as you change blocks and the mapping from x+y screen coordinates (click, arrow up/down across block boundaries) to text field offset is doable by writing a shim for caretPositionFromPoint.
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.
How did you capture and render text input, and how did you handle new lines when the text wrapped? How do you handle paragraphs, especially when a user would delete text?
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.
> How did you capture and render text input, and how did you handle new lines when the text wrapped? How do you handle paragraphs, especially when a user would delete text?
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.
Indeed. You can roll your own without React. Simply indicate when something changed in your model, and update the DOM on the next frame using a PURE state -> DOM mapping function.
It's Backbone. A pure state -> DOM function is also called a template. It works when the app is simple. The problem is that you do NOT want a pure state -> DOM function. It blows away state of things like mounted subcomponents and input fields. Look forward to lots of ad-hoc DOM coordination as your app grows.
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.
Not at all. I've written/cleaned-up a few dozen Backbone apps. They work great when they're small and are generally a mess once they scale. An example: how do you use a datepicker component in your pure state->DOM system and guarantee setup/cleanup of DOM event listeners?
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.
Yes, if you need to add a behavior to the UI which "can be dropped anywhere", such as drag-drop, or rich text editing, then as I said, you're going to have to read from the DOM and otherwise try to adapt "to any reasonable environment" with varying degrees of success.
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.
And for simpler cases one doesn't even need to detect changes in the model. Just do a debounced re-render the virtual DOM on any incoming event/timeout/network activity:
For those cordial with CoffeeScript, there's Hamlet[1] (which is based on/inspired by HAML[2]). It's a pretty nice declarative-esque UI library. From their home page, here's a sample:
The convergence of the designer and programmer roles feels, if you'll pardon the pun, like React's one-way data flow: moving from design to programming.
Of course, the ecosystem will build better tooling to support less technical folks, but for now it's still heavily engineering-focused.
Now, it would be interesting to see how function pay() should be written with the new approach and how it evolves when the new states ("network went away error", "must be logged in", "certain role", "pre-existing data") are discovered.
I wasn't able to read any of this because of the stupid fucking flashing view counter at the top of the page.
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.
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.
[1] http://reagent-project.github.io [2] https://github.com/Day8/re-frame/