question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Component encapsulation by default

See original GitHub issue

My main issue with the current Cycle architecture is the way drivers are actually globally scoped:

  • DOM.select('.field') looks for an element in the whole DOM subtree managed by Cycle
  • HTTP is the stream of all HTTP requests/responses going through the driver

The developer has to be aware of this and take extra precautions to filter out other inputs, such as other parts of the DOM also matching the selector, or other requests issued by other bits of the app.

The HTTP and fetch drivers encourage filtering the stream of requests by URL or name, but that solution is suboptimal because it could easily suffer from clashes if other components requested the same URL or used the same identifying name. Put otherwise, there is no strong guarantee of isolation.

The better, driver-agnostic solution to this problem is isolate (provided the driver implements isolateSource/isolateSink – the fetch-driver doesn’t for example).

It could be argued the implementation of isolateSource/isolateSink is somewhat inelegant compared to the rest of Cycle, with the HTTP driver patching a _namespace property on the request, and the DOM driver writing class names that end up in the DOM (see https://github.com/cyclejs/core/issues/226), but I guess these are mostly hidden away from users.

However, shouldn’t isolation be default behaviour, possibly with an opt-out, rather than being opt-in?

Anecdotally, of all the people I talked to this about, no-one had realised that sources.DOM.select(_) was not scoped to the virtual-dom subtree returned by that component.

I really love the concept of fractal architecture @staltz is striving for, but I would argue one of the key benefits is to reduce cognitive load and complexity for the developer by allowing local reasoning, without side-effects. Without isolation, there’s a persistent risk of global “side-effect” (in the broad, non-FP sense) and hard-to-debug collisions between distinct parts of the system.

Another way to phrase this is: when is isolation not desirable? Surely, components should always be isolated, unless they rely on side-effects (watching DOM they don’t own, catching requests they didn’t issue), which is antithetical to the whole concept.

These practical concerns aside, I’m also curious about the performance and scalability implications of having such centralised management of streams. Are there benchmarks of how this scales up to thousands of components, with hundreds of subscribers to the HTTP driver requests stream and thousands to the DOM driver events streams, recursively calling the isolateSource and matchesSelector for each level of the component tree?

I’d like to make it clear that I still think Cycle is a fantastic idea, and the reason I raise this is because I’m worried this could get in the way of its success.

To make this a more constructive criticism, I’ve also played with an alternative DOM driver API that keeps effects local. I’ve put it up in a repo on GitHub, in the hope it can spur discussions on alternative solutions to this issue.

I admit I was also trying to make observed events more explicit in the component sinks, rather than relying on .events() on the sources and manually matching class names in the .select(_) and outputs. However I’m interested to hear what people think, what principle I have violated doing so, and what may be wrong with this approach 😄

/cc @jamesgorrie @kenoir @phamann @oliverjash @jackfranklin who I discussed this with in the past few weeks.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:1
  • Comments:89 (58 by maintainers)

github_iconTop GitHub Comments

3reactions
staltzcommented, Mar 14, 2016

Is that really true though? Looking at the code, events() actually results in a element.addEventListener on the root element. Yes it is lazily evaluated only once the streams are consumed, but it still feels like this operation on the sources results in a write-effect on the DOM (addition of a listener), which seems to violate the Cycle design goals?

The fact that it is lazily attached to the root is implementation detail. If we would have attached it eagerly on the root when the dom driver is created ( so not anymore on the first call of select().events()), it wouldn’t make any difference at all for the application developer.

The point here is: conceptually this is an entirely harmless side effect and I have no idea why we discussing this as a problem. It is not.

button('.handlePlus', {'on-click': 'plus-clicks'}, '+')

Now we just lost an important property.

Better respect the Cycle principle of making write-effects explicit in the sinks

Because now we have read effects (click listening) encoded in the sinks. and this is a real problem because the virtual DOM rendering isn’t anymore just about rendering a visual output to the user, it is also about programming what happens when the user interacts (read effects). In practice if I want to code the users interaction, I should only write code in the intent, which determines what events are interesting. But in your suggestion, now those"interesting events" (e.g. click) are encoded in the view. It undermines the foundation.

2reactions
staltzcommented, Mar 18, 2016

Ok I see you want parent be isolated from childs things, but I’m not actually sure that is aways desirable, sometime you want parent know what is children are doing.

Yes, I agree on that, and I remember it being one of the motivations for keeping it like it is. Isolation is primarily between siblings. The parent technically should have control and visibility over everything that comes in and goes out.

Read more comments on GitHub >

github_iconTop Results From Across the Web

View encapsulation - Angular
Emulated is the recommended and default mode. ViewEncapsulation.Emulated, Angular modifies the component's CSS selectors so that they are only applied to ...
Read more >
How default view encapsulation works in Angular
There are three types of encapsulation in angular. ViewEncapsulation.Emulated and this is set by default; ViewEncapsulation.
Read more >
Templates, Styles & View Encapsulation • Angular - codecraft.tv
By default styles for our components are encapsulated, that means that they don't leak out and affect the rest of the application. We...
Read more >
View Encapsulation - Rangle.io : Angular Training
Angular provides three encapsulation strategies: Emulated (default) - styles from main HTML propagate to the component. Styles defined in this component's @ ...
Read more >
Understanding Angular View Encapsulation - Telerik
In this post I'll introduce you to Angular view encapsulation and show why each member is important and how and when to use...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found