Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>My "thing I dont know as of 2018", just for good measure: How to build complex front-end applications without making a complete mess of things!

If you find anyone that truly knows the answer, please let us know :)

I'm sure plenty of people claim to, and many more will just claim that the mere concept is flawed so there's no point, and there are even more non-answers. But the actual problem is very difficult. I remember when AngularJS (the old Angular) first came out and it felt like we finally started having an idea of how to handle frontend after years of dissatisfaction with jQuery, Knockout, etc.. Then React, Polymer, Web Components, ES6, Webpack, Modules, TypeScript, Redux (and its Flux-based ancestors) and so, so much more hit and... it's mesmerizing.

I think React is the best option so far, it's still not 100% where it should be but it offers my favorite API and the essence of it is simple enough to fit in a tiny, minimal library if you want. It can be tricky here and there, but the way it encapsulates state is, for the most part, super predictable. I love the way you fold asynchronous streams into state and props using tools like create-subscription so that components don't have to fiddle around with confusing, long async chains.

For me, Polymer and Web Components were both huge misses - the promise of Web Components is still neat to this day, but I really don't think I would want to compose my application out of many Web Components, it just doesn't seem like the right tool for that job, it seems like a better tool for embedding or shared widgets. There's really a lot that can be said here w.r.t. Polymer and Web Components in general but I don't think any of it hasn't been said better so I'll leave it alone.

Angular 2+ do not do it for me. It is amazingly nice having so much out of the box unlike React, but at the same time I find myself constantly annoyed. The NgModule system feels like a relic of AngularJS. When it was standard to simply concatenate JavaScript files together and call it a bundle, this system made perfect sense. In the world of ES modules and Webpack, it's just a layer of needless complexity. The AOT compiler causes all sorts of shenanigans where valid, obvious JavaScript won't work. I don't really care for dependency injection, at least not the way its implemented in Angular. It does give you some neat tricks, but I am happier with my obvious, ugly code, thanks. Even disregarding modules and DI, I still don't like Angular. My favorite concept from React is that the tree of components is out of line of the DOM. In Angular the component is actually in the DOM. This usually only matters in a few cases, but when it does it's really annoying. Example would be CSS, or say, if you want a component to be a table row. The way event listeners work is not orthogonal between child elements and the so-called 'host' element due to this, as well, whereas in React you can just use HTML-style on event attributes since you are rendering all of the elements that end up in the DOM always. Angular's documentation frequently doesn't have example code showing you how to use things, which may actually be because the developers aren't sure - I've often tried to figure out how to use basic features only to find GitHub issues pointing out the severe limitations in them. Like, Angular Router - What if you want to compartmentalize some routes in a child module? You could of course just define a Routes[] variable somewhere and import it, but there is actually a RouterModule.forChild, so surely you can use that? No. You can only use that if you are using loadChildren, which uses lazy loading. Lazy loading is actually a PITA especially depending on how you have modules setup, and the AOT compiler once again does some truly confusing stuff. Like, you can fake loading something synchronously with loadChildren, but check out what you gotta do to make it work with the AOT compiler: https://github.com/angular/angular/issues/10958#issuecomment... - Nearly every interaction with Angular beyond trivialities end in multiple open GitHub issues that lead nowhere, and it's frustrated me like crazy. Angular also seems to like RxJS, a library I really want to love but can't seem to quite get there. It's very powerful, but I hate that to get the exact behavior I want I often end up with quite a long list of operators where the order can be important in subtle ways. It's easy to leak subscriptions in RxJS especially if you're new to it. Some things are hard to implement, like say if you wanted to implement some kind of feedback loop where the result goes back through. And worst of all, you tend to get RxJS subscriptions and subjects at the component level at Angular, meaning you've got to deal with these async values in rendering code, in logic code, etc. Which can cause RxJS to spread like a virus when all you wanted to do was pull some state in and combine it with some other state. And if you want change detection to work, there's even more rules you need to be careful about following...

I've yet to try Ember. I've looked at Vue.js but ultimately haven't been as drawn in.

...But even despite my preference for React, large apps are still super hard to structure, and many problems feel unsolved. Like saving and synchronizing state to a server. There's individual solutions to that problem, but none feel like they're 'perfect' across all of the domains you want them to be. GraphQL seems like it could be nice but so far I have been mystified by it in practice, and longing for better type-safety on both sides. React obviously is far from perfect, too, and because of its imperfections, you are going to want a lot of linting to prevent people from shooting themselves in the foot, especially if you are working on a team. I've never fully figured out unit and integration testing in React, last I checked the standard was to use Jest and JSdom and JSdom required native Node.JS modules and bla bla... needless to say, I was unsatisfied. Because unit test suites on JS apps tend to compile to one giant bundle, you have a harder time parallelizing work and doing coverage-based tests.

Organizing components? Smart components vs dumb components? Container components? High order components? Where to put the Redux reducers and actions and state? How to split your bundle? Whether or not to split your bundle? Webpack vs Rollup vs Parcel? I'm barely scratching the surface. Production frontend apps are insanely difficult and it's fragmented to all hell. I don't buy in super hard to the JS fatigue thing, but I do think we're very scatterbrained right now on how to build good frontend apps, and it seems like it's gonna take a while to fully collect our thoughts and figure out how things 'ought' to be done. Right now, it's really super all over the place.

So yeah... I think it's OK if you don't know how to write complex front-end apps without making a mess. I doubt you are alone even among the pioneers.



A few things that have made creating new React projects easier for me:

- create-react—app or Parcel bundler, both of which require zero config. CRA is easiest but I’m not a fan of their defaults for Typescript (which is to prevent the app loading in dev if you have any tslint or compiler errors, even if they are just warnings) so once I’ve messed around fixing that, it’s almost just as easy to use Parcel.

- For website projects, I use react-static as a static site generator as it can handle all your data loading, bundle splitting, server side rendering, etc. for you automatically. It’s best to start your project with react-static rather than retrofitting it, they have a command line tool to set up a new project so this is usually instead of CRA/Parcel. Gatsby is probably an equally valid choice, react-static seemed a little simpler to me and I’ve been happy so far but Gatsby community seems larger.

- I’ve not used Redux for a couple of years now, instead using MobX for application state. Personally I find it so much quicker and easier to use, although it is undeniably more “magic”. Honestly think this is one of the biggest productivity enabling changes I’ve made since switching to React.

- Personally I’m a fan of Typescript, while it can be a bit of a pain initially, I think you reap the rewards as a project progresses in terms of ability to refactor easily and avoid wasting time on syntax errors.


I disagree with CRA because it promotes developers avoiding and fearing configuration files. If you are a full time front end developer and your shop uses it you NEED to learn Webpack/Babel/NPM/etc because it is a short up-front investment, will save significant amounts of time, and will lead to a noticeably better product.

We're talking about hours of study leading to a long-term hundreds of hours of time saved.


Devs shouldn't have to spend hours setting up build configuration just to get started learning React, or every time you have a side project you want to try out. Granted, you _never_ needed Babel and Webpack _just_ to use React, but for a long time every React tutorial started with "First, we'll learn how to set up Babel and Webpack". Now, they can just say "Run `npx create-react-app my-app`", and immediately get a solid project setup that works out of the box.

Nothing prevents you from still writing entire Babel and Webpack configs by hand if you want to, but it shouldn't be a prerequisite for actually using React (or any other framework).


Couldn't you make this same argument about math curriculum? "Why should we teach algebra when its so easy to just use basic calculus for real world problems".

The problem is there is a huge gap between a development react application and a production react application that is served to users with best security/operational processes. The own creators of CRA do not recommend it be used in production and isn't that the goal for most react developers, to put apps into production?


On the contrary, CRA's docs specifically recommend it for production use:

> No Configuration Required: You don't need to configure anything. A reasonably good configuration of both development and production builds is handled for you so you can focus on writing code.

Nowhere in the docs do I see phrases like "don't use this in production".

As for the math analogy: I'd say the difference is that many calc concepts do require understanding of algebra to grasp and use, whereas the only thing about React that _sorta_ implies knowledge of Babel is the JSX transform, which isn't a hard requirement to use React but is certainly the common preferred approach.

Again, I'm not at all saying that people _shouldn't_ learn how to set up build tool configs. I agree that's valuable knowledge to have. I'm just saying it shouldn't be a hard blocker for using React, and that React tutorials shouldn't have to spend half their time teaching unrelated build tools instead of React itself.


As a new web developer: what can I do once I know how to config these things, that I don’t get as part of CRA?


Off the top of my head, I'm sure I'm missing a few obvious ones. Notably CRA doesn't prevent you from doing any of these, but taking the time to learn the various build tools will better enable it.

* spend less time debugging dependencies across version changes

* reduce bundle sizes

* create a better CI/CD pipeline

* reduce unnecessary dependencies

* fork/fix/create your own plug-ins and other tools

* self-hosting npm to avoid production/dev outages

* easily learn new tools because you understand the underlying systems/alternatives


In case you missed it, the official create-react-app 2.x supports TypeScript out of the box now, and it has better defaults than react-scripts-ts. No more ridiculously strict lint rules.


My primary background as a professional developer is front-end, and I sometimes feel I can barely keep up. For any project where it's an option, I try to put as much in the back-end as possible. So far it's working out well.

For example, I had a little game where initially I wrote a React+ecosystem solution that worked. Then, considering that I don't need x updates per seconds, I switched to server-side rendering + morphdom.

As a result I could reduce all my code to 1) websockets messages to the server and strings of markup from the server, and 2) a few 'optimistic updates' on the client-side by directly changing the DOM. Having no more client-side state to maintain really simplifies things.

Considering that this was a viable solution to a game that has to update the DOM on pretty much every click (but no more than that), it's probably a fine solution to the vast majority of apps I build.

I can't wait for Phoenix LiveView to make this kind of solution less ad-hoc, but at least so far even the ad-hoc solution is so much simpler...


You definitely aren’t restricted to lazy loading when using RouterModule.forChild, idk where you got that, just define a module, add forChild with your routes, and import it into another module. Im doing this all over our codebase.

I can understand you’re opinions about the module system, but I disagree. For a small to medium size app it can seem like annoying boilerplate that gets in the way. If you’re working on a large app across multiple teams then it’s very helpful. You have a single entry point for all relevant code. You can define the api for that module so other teams don’t misuse what you have built. It can get frustrating when you have a single component that you want to share and you need to wrap it in a module, that feels very unnecessary, but I know the angular team is working on that.

I actually really like that my components are in the dom, I can see clearly where it is, not which div with which class or Id it is. It makes inspecting the tree very easy.

RxJS can be difficult and hard to learn, which doesn’t help that the docs are meh and you can never find exactly where to go. Juniors really need someone to help them not leak subscriptions. Though if you follow Angulars promoted idea of using the async pipe, then that will automatically clean up your subscriptions. Again, for small components/apps it feels way overkill, but it feels oh so nice when your trying some complex async stuff and can handle it and pass it along so easily.


>You definitely aren’t restricted to lazy loading when using RouterModule.forChild, idk where you got that, just define a module, add forChild with your routes, and import it into another module. Im doing this all over our codebase.

Genuinely, I have not been able to figure out what it actually does from the documentation. Either it isn't there or I have been holding it wrong.

Because, loadChildren actually would import the routes from a child module as children of an existing route. But what you're saying, it sounds like the child routes would get placed at the root of the tree. I don't want that though, that doesn't allow me to do the decoupling I wanted to do. I don't fully understand why I can't just specify a module for children when loadChildren will happily take one asynchronously.

>I can understand you’re opinions about the module system, but I disagree. For a small to medium size app it can seem like annoying boilerplate that gets in the way. If you’re working on a large app across multiple teams then it’s very helpful. You have a single entry point for all relevant code. You can define the api for that module so other teams don’t misuse what you have built. It can get frustrating when you have a single component that you want to share and you need to wrap it in a module, that feels very unnecessary, but I know the angular team is working on that.

I work on only relatively large teams and I still don't get it. If I didn't want someone to be able to use part of my API, I would just not export it. Further, systems like Bazel already provide package visibility, which is a lot more powerful in terms of limiting the spread of an API imo. I know in Angular sometimes things have to be exported because of the AOT compiler, but that's not a great justification.

>I actually really like that my components are in the dom, I can see clearly where it is, not which div with which class or Id it is. It makes inspecting the tree very easy.

I would've thought it would make this easier, but due to the amount of power you have in the selector for components and the way directives work, I've still definitely found myself confused at times. At the end of the day, I didn't really have a complaint with the way React handled it, aside from the inconvenience of needing a separate debug tool to see the component tree.

>RxJS can be difficult and hard to learn, which doesn’t help that the docs are meh and you can never find exactly where to go.

To be honest with you, I found the RxJS docs to be OK. It is a bit hard to search them, but most of the information I actually needed was available if I did. I personally had more trouble finding answers with Angular than RxJS.

>Juniors really need someone to help them not leak subscriptions.

I don't like the way you've worded this because it heavily implies that it's mostly only an issue for junior developers. However, I've seen some very tricky takeUntil setups that are surprisingly nuanced. If your async chains are simple it's easy to define the lifetime, it's not so easy when your chains lifetime may be a subset of your components or services lifetime.

Interestingly, if we had things like higher order components, we could turn the problem into a component lifecycle problem, which is what React likes to do. I miss being able to encapsulate problems like that.

>Though if you follow Angulars promoted idea of using the async pipe, then that will automatically clean up your subscriptions.

I use the async pipe where possible because it does offer a reprieve from manually managing things like lifespan. Sadly, there are a lot of times where using the async pipe is not super nice. Let's say a value that was synchronous has become an observable, now if I was using other pipes or using the dot accessor suddenly it becomes more complicated. Generally I just give up at that point.

>Again, for small components/apps it feels way overkill, but it feels oh so nice when your trying some complex async stuff and can handle it and pass it along so easily.

With the caveat that it actually does not map all complex async problems elegantly.

It is super nice for some common cases like implementing type-ahead, and I generally use RxJS even outside of Angular nowadays because of the fact that it's just easier to express complex asynchronous behavior with it, but there have definitely been moments when I realized my somewhat complicated RxJS code could be replaced with relatively simple use of async/await and a for loop :|

I have a very large number of Angular gripes other than what I listed, so please don't take it to be exhaustive. I just wanted to illustrate in great detail the ways I did not like Angular because if I don't do that many people will assume it's because I didn't give it a fair shake.


There is a core issue with UI development that makes things harder and less elegant than the backend.

It's the io loop. Components must change state and react to state. This where the complexity arises. If you remove the loop by having components only react to state, you will find your programs to be simple, modular and more beautiful.

The feedback loop of IO destroys the functional and modular nature of UI. Functional programming simplifies programming through restriction, however ui development is inheritly an object oriented problem due to the IO loop, so the results of trying to make it functional will have limited benefites.

The way to make UI work is to find a way to have components react to state and change state in a simple way and without hard coding a refrence to external state in the component itself. Aka stateless.

Many people believe that passing a closure is the solution. The reality is passing a closure is awkward and a form of encapsulating state with methods that leads to the same issues of complexity that you get with oop. Keep data and functions seperate always, a closure is technically a functional concept, but it is also oop in disguise as it is instatiating state coupled with a functions just like an object in oop.

Global event emitters might be the best way. I'm not sure if redux does this. I'm not a UI guy so everything is just imho.


I don't think many people actively working on front end will agree with passing closures as the best way. "callback hell" is a very common complaint.

What you've discussed above seems to overlap pretty well with both redux and monadic state management like Elm.


Hmm maybe elm isn't really monads. Tried to bullshit there, my bad. https://redux.js.org/introduction/prior-art#elm


>however ui development is inheritly an object oriented problem due to the IO loop

There's nothing OO about the IO loop. The main thing that calls for some OO in UIs is the widget hierarchy.


IO loop necessitates OO unless you do some obscure tricks like passing closures and using a global event emitter. The most obvious way to implement UI is to do OOP.

Widget hierarchy does not necessitate OO. Hierarchies can be built with plain data structures. OO comes into play when you place methods within the data structure. Methods coupled with data is essentially an object as described by OOP.

Why would you need integrate a method inside data structures?

Because IO.


Those claims are somewhat confusing. What is the difference, in your mind, between an IO loop and a "global event emitter"? That's all the IO loop is; an event emitter that fires events at subscribed code.

Do you consider the "OOP"-ness to be evident in the fact that subscription functions (whether 'onclick' functions or 'fetch' callbacks) contain both code to react to events and an implicit binding to the IO loop? Or is your issue that some (not all!) DOM APIs happen to let you attach on-event functions to the same things you can read data out of?

Basically, I don't see why UI programming is any more inherently OO or functional (assuming those are even opposites, which has historically been debated more or less to death) than any other domain. There are UI toolkits that subscribe to each and both of those paradigms, with varying amounts of rigor.


When I talk about an IO loop, I mean external state. The program sends a message to external state, the external state sends a message back. That is a loop. If you get rid of one, you no longer have a loop. you just have IO. If you get rid of both you have just data and no IO. I am not talking about abstractions on top of this loop. A global event emitter is such an abstraction that one can use to hide and block off the impurity of IO from the rest of your code. The most primitive form of IO is the socket.

>Do you consider the "OOP"-ness to be evident in the fact that subscription functions (whether 'onclick' functions or 'fetch' callbacks) contain both code to react to events and an implicit binding to the IO loop? Or is your issue that some (not all!) DOM APIs happen to let you attach on-event functions to the same things you can read data out of?

Look at where events come from and where they are sent. How do you associate the action of button A with the data representing button A? The most obvious way is to merge the action and the data into a single primitive. An object as described by OOP. You will have to do this regardless of what an API such as the DOM offers as primitives.

>(assuming those are even opposites, which has historically been debated more or less to death)

They are opposites. This is only up for debate for people who don't truly understand the differences. In OOP as described by JAVA or C++ you have a graph of mute-able objects instantiating other mutable objects and modifying each other. In functional you have a linked list of composed immutable functions with an input on the head and an output on the tail. Nothing is mutated. To get functional working with UI you need to employ obscure tricks as a UI widget is more of an object than it is a function.

They are opposites according to the most common definition of OOP. They are only orthogonal if you get rid of mutation which is NOT the way most OOP is used nowadays.

>Basically, I don't see why UI programming is any more inherently OO or functional

A UI widget cannot just be a function. A widget in-itself is data, the structure, the display, the color ... etc. And It must react to state and IO like a function. It is both data and methods and thus OO.

Without IO the the widget can just be a plain data structure. HTML is basically pure data. You can still keep it functional if you have a function dynamically generate HTML based on a set of parameters (IO without a loop). However when you need to introduce the "loop" and have UI both send and receive data that's when methods need to be attached to the UI widgets.


That's a baffling definition of IO and IO loops. It sounds like you think bidirectional IO is an IO loop, as opposed to unidirectional IO (which . . . does that even exist outside of "write bytes here" shared memory constructs? Even UDP write operations get data back from the kernel), but you also classify a socket, which is typically bidirectional, as just "IO", so I'm not quite sure what to make of that. It sort of sounds like you're talking about asynchronous IO, which is often coupled with event loops (which may or may not be IO loops unless you define time-based conditions as IO) in practice, but doesn't fundamentally have anything to do with them. Is that what you mean?

When you talk about an event emitter, what kind of code structures are you referring to? An EventEmitter a la Node.js is a pretty decent separation of IO and data, though not perfect: something external (perhaps an event loop) triggers subscribed code based on IO, but your code is given only data (payload) to process. Is your beef that 'someThing.onEvent(eventType, e => whatever)' is more 'OOP' than 'bindEvent(someThing.eventType, e => whatever)' is 'functional'? If so, how or why?

In general, it sounds like you might be referring to typical JS asynchronous IO (callbacks/promises/async-await and whatnot) when you talk about IO loops. But I can't for the life of me figure out what those things have to do with OOP vs functional programming, nor why using those structures requires you to store state on a "widget".

> Look at where events come from and where they are sent. How do you associate the action of button A with the data representing button A?

I can say 'button.onClick(() => { button.clicked = true })' just as easily as I can chain every computation which would happen as a result of 'button.clicked' being true onto the event instead. The former is more OOP, colloquially, than the latter, but is definitely not inherent to UI programming: tons of popular patterns and frameworks exist whose primary goal is to remove the coupling between state and UI appearance.

> In functional you have a linked list of composed immutable functions with an input on the head and an output on the tail. Nothing is mutated.

This has nothing to do with functional programming, or a functional style. Immutable data exists as a very common pattern in both colloquially-FP and colloquially-OOP languages, and so does mutable data.

At the point where you start talking about linked lists, I begin to think that you're misunderstanding or misusing some fundamental term we're discussing (Mutability? IO? Functional? Object? I'm not sure): a LL is a data structure (which is mutated by adding/removing elements, traditionally), and has absolutely nothing to do with programming style or paradigm.

> A UI widget cannot just be a function. A widget in-itself is data, the structure, the display, the color ... etc.

No, a data object can contain those things, but in higher-level UI programming, the data object is very very rarely the "widget" itself; it's just data that is supplied to the renderer (the API of which, in web UIs, is the DOM), and the renderer generates the UI based on that.

You can do that in a tightly coupled way and store all your state in the DOM, or you can use any one of many powerful systems that allow you to separate those and perform operations only on data, which can be applied to the external UI state outside of your code. React is a good example of this.

> HTML is basically pure data.

I think you're confused about the difference between HTML (text, can be generated and passed around by code) and the DOM (the API to what is being rendered by the browser). HTML is data, sure.

> You can still keep it functional if you have a function dynamically generate HTML based on a set of parameters (IO without a loop).

That's what event listeners and callbacks are. Whether or not you're dynamically generating HTML, or whether code is mutating it after it's loaded into the DOM, has nothing to do with functional-or-not. Code doesn't stop being 'functional' as soon as its goal is to alter the state of or handle input from some external thing, whether that's a DOM node or a traffic light.


>That's a baffling definition of IO and IO loops. It sounds like you think bidirectional IO is an IO loop, as opposed to unidirectional IO (which . . . does that even exist outside of "write bytes here" shared memory constructs? Even UDP write operations get data back from the kernel), but you also classify a socket, which is typically bidirectional, as just "IO", so I'm not quite sure what to make of that. It sort of sounds like you're talking about asynchronous IO, which is often coupled with event loops (which may or may not be IO loops unless you define time-based conditions as IO) in practice, but doesn't fundamentally have anything to do with them. Is that what you mean?

I don't even know where you're getting all this from. I defined the "IO loop" the access of external state as both a read and a write. Once I defined it, it should be clear what I'm talking about. You should be aware of my definition through my explanation regardless of implementation. You go on to talk about asynchronous IO which is a whole different thing all together. I am not talking about concurrency. I am talking about IO from a very high level perspective.

Let's simplify it. Imagine an API that reads and writes strings from IO:

  func read(source: str) -> str
  func write(source: str, data: str) -> None
If you have both of these things in your program you have an IO loop. If you don't like the wording, give it another name. Either way, this "IO loop" is what I am addressing in the conversation. If I have a button widget that does IO, say... write data on click, then the most obvious way to create that widget is to unify the data describing that widget with a method that calls "write." When you unify data and methods you get an object. You understand now?

>This has nothing to do with functional programming, or a functional style. Immutable data exists as a very common pattern in both colloquially-FP and colloquially-OOP languages, and so does mutable data.

This is completely wrong. Mutable data is not part of the functional programming paradigm. Here's the definition straight from wikipedia:

"In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."

>No, a data object can contain those things, but in higher-level UI programming, the data object is very very rarely the "widget" itself; it's just data that is supplied to the renderer (the API of which, in web UIs, is the DOM), and the renderer generates the UI based on that.

Obviously, we're not giving pixel level instructions (aka the actual widget) to the ui renderer. The renderer still needs to associate what's rendered with a function. A widget is not just a visual representation of something; it is an interactive visual representation. Data needs to be unified with methods to fully define a UI widget. Hence Object. As I will mention in other responses, this is the most obvious and common way of handling the UI problem. There are other methods that don't use OO. But a UI widget and what it does has a one to one match with the definition of an object in object oriented programming.

>I think you're confused about the difference between HTML (text, can be generated and passed around by code) and the DOM (the API to what is being rendered by the browser). HTML is data, sure.

I am perfectly clear about what I am talking about. There is absolutely no confusion. I am perfectly aware of what you just said. HTML is data, the DOM is an api that can manipulate said HTML.

>That's what event listeners and callbacks are. Whether or not you're dynamically generating HTML, or whether code is mutating it after it's loaded into the DOM, has nothing to do with functional-or-not. Code doesn't stop being 'functional' as soon as its goal is to alter the state of or handle input from some external thing, whether that's a DOM node or a traffic light.

When did I say the DOM was functional? I'm not talking about the DOM. I'm talking about UI development in general from a very generic perspective. I'm not looking at UI development soley from the perspective of javascript, HTML and the DOM. What I was talking about in the statement was this:

   input_parameters = get_io_stuff()
   renderUI(input_parameters)
Also this statement:

>Code doesn't stop being 'functional' as soon as its goal is to alter the state

is completely and utterly wrong. Functional programming by its nature avoids all state changes. If it doesn't avoid state changes then it hides state changes through blackbox abstractions.

>I can say 'button.onClick(() => { button.clicked = true })' just as easily as I can chain every computation which would happen as a result of 'button.clicked' being true onto the event instead. The former is more OOP, colloquially, than the latter, but is definitely not inherent to UI programming: tons of popular patterns and frameworks exist whose primary goal is to remove the coupling between state and UI appearance.

This is exactly what I'm talking about. I never asked for the specific implementation in javascript, It's baffling why you bring it up. Here's what I'm saying: I'm saying the former OOP method is the most obvious method, the later method is using a global event emitter. The latter method is better because it makes the UI widget reusable across systems that utilize the same event emitter while the OOP method ties the handler and a reference to IO to the widget itself. Thats it. I think we can both agree that the majority of systems utilize the OOP method due to the prevalence of that style of programming.

>When you talk about an event emitter, what kind of code structures are you referring to? An EventEmitter a la Node.js is a pretty decent separation of IO and data, though not perfect: something external (perhaps an event loop) triggers subscribed code based on IO, but your code is given only data (payload) to process. Is your beef that 'someThing.onEvent(eventType, e => whatever)' is more 'OOP' than 'bindEvent(someThing.eventType, e => whatever)' is 'functional'? If so, how or why?

Yes. This is exactly what I'm talking about. I am also saying the latter method is better. It is better because it allows me to take that widget and reuse it in another project with a very different IO patterns. Imagine two implementations for button. We have code like this:

One implementation requires this: button.onClick(() => { button.clicked = true }) Another requires this: button.onClick(() => { button.color = "red" })

Try to think of onClick as just something that registers a handler to when the button is clicked. Ignore all the lower level details of what actually happens underneath and think of each as a method definition.

The code is not re-useable in general. Usually if I implemented the first requirement and hit the second requirement I have to write a lot of code to make an abstract version of button in which I can inherit two buttons with two different handlers.

Now look at this: button.onClick(() => { sys.triggerGlobalEventName("button.clicked") })

This makes the button reuse-able in any system that has sys.triggerGlobalEventName. The typical way to handle this issue is usually through inheritance and polymorphism. That style of abstraction leads to over complexity.


> Global event emitters might be the best way. I'm not sure if redux does this.

I think Redux actions are essentially emitted global events.


That's one way to look at them, yes.

Redux's `store.subscribe()` function is obviously a general event emitter, where the only event is "an action was dispatched", so it doesn't even need a name.

On the actions side, Redux really only has a single "root reducer" function. Since having a gigantic monolithic function for all state updates would be unmaintainable, we split that function up into smaller functions, like any other piece of code.

Since Redux was based on the existing "Flux" concepts, the idea of having multiple "stores" for each different type of data was translated into having multiple "slice reducers", each one responsible for independently updating the data at a given key in your state object. Redux provides a `combineReducers` utility for this use case, which iterates through all the keys in the state object and calls the slice reducer at each key.

If you squint at this reducer setup the right way, you can see it as being a limited pubsub event system as well.

So, it's reasonable to view a Redux store as being a 2-way pubsub system. Different parts of the app dispatch an action to "publish" an event to the mostly-independent slice reducer functions, and the store publishes a single event to let any part of the UI know that the state _may_ have been updated.


This is a mega-comment. Do you blog ?


Word!


Brilliant.




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

Search: