GithubHelp home page GithubHelp logo

Comments (38)

Westbrook avatar Westbrook commented on May 30, 2024 6

@sorvell did you ever get anywhere with your draft of a proposal here? We're looking at formulating a solid "How do I make apps without the context API?" resource for engineers at Adobe moving from React to Web Component projects and would love to shape it around something that was closer to a "community protocol". I'm sure we'll need a good amount of iteration on anything we bring together here, but having something to poke holes in would be a great first step!

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024 2

I have begun documenting my current approach to Context here in this repository: https://github.com/benjamind/lit-context

I've outlined my best guess at what the API should be, and also provided some implementations against lit-element v3 style controllers to make it convenient to use.

Would love some feedback on this.

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024 2

I'd definitely like to hear more about what failed you with regard to use of callbacks @jarrodek . One of the benefits for me of this approach is that it is very easy to reason about, and extremely simple protocol. It is also very easy to implement it synchronously, while not closing the door on asynchronous value fulfillment, which I think for many scenarios is quite valuable.

I think I can see what you are driving at in terms of state update, making it an explicit action of the component to 'get' its updated value in response to an event emitted by the state store. I suspect this is not incompatible with the API as proposed above and could be built atop this to provide a different approach to state management.

I've added a few goals/non-goals to the readme in the repo linked above. I've specifically called out wanting to keep this API as simple as possible, a variety of things could be implemented using this protocol, but I think at its core we should strive to keep this as straightforward as we can for maximum compatibility.

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024 2

@mihar-22 and @UpperCod thanks both for the detailed input!

I think perhaps we are going down similar lines here. I've had more discussion with a few folks who have suggested similar approaches and I am in general agreement that tightly binding consumer and provider is the way to go now.

Please take a look at the initial implementation of the context protocol as defined in this PR lit/lit#1955

I do also agree a decorator implementation would be desirable and is next on my list for inclusion into the lit context PR. It is however not something we should include in the protocol spec I think since it's very specifically an implementation detail that might be best tailored by libraries specific to component implementations.

I will try and find time to revise the proposal to include these changes in direction and incorporate some of the ideas in your proposals.

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024 1

@Westbrook

One area that I've struggled with a little bit when thinking of similar is the idea of live data without being tightly coupled to a renderer

This should be pretty well addressed by the event carrying a callback. The provider calls the callback with data, the consumer reacts however it needs to, including triggering a render. If the consumer supports it, the callback can be called multiple times and trigger updates each time, very similar to useContext in React.

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024 1

@ryansolid

The one thing I've wondered with this event sort of approach is timing.

Events are synchronous, so the timing is suitable for data needed synchronously.

To use events am I correct the element would have to be connected to the DOM?

Events work in disconnected DOM trees, so the elements would not necessarily need to be connected to the document, though a provider would need to be an ancestor in the disconnected tree to provide anything. The question there is what signal the element would use to fire the event to request the data? connectedCallback is probably the most natural place, so if you needed something even when disconnected you'd have to fire in the constructor, attributeChangedCallback, etc.

Would the data be available to us at the time of connectedCallback?

Yep, if the provider is ready. One nice thing about firing the event in connectedCallback is that if you are requesting subtree-dependent data, you can re-request if the element is moved to a new subtree.

The other thing is whether slotting should have any consideration. Can light DOM children slotted under Shadow DOM Provider still get access to context.

Event bubble up the flattened tree, so slots and slot ancestors, including the host, could act as providers to slotted children. This can enable use cases where a utility element in a shadow root, maybe like a theme provider, could provide objects to a slotted child.

I think events, their timing, and their scoping are really well suited for this problem.

from community-protocols.

EisenbergEffect avatar EisenbergEffect commented on May 30, 2024 1

While I find Context APIs handy, I've found them to not be quite enough in larger applications. In most cases, I've used a more robust Dependency Injection system. Not too long ago, I did some work to integrate a DI system I had written so that it worked on top of React's context. I'd really like to ensure that I can implement DI on top of this Context proposal in a similar way. I imagine that it would work by:

  • Using the protocol to locate the container itself, rather than a particular service.
  • Having the located container also register for context events and resolve all requests from 3rd party components itself.

The first point should allow 3rd parties to introduce their own container into the DOM hierarchy to resolve services requested through the DI system, while the second point should allow the DI system to resolve requests by components that are based on the raw context API with no knowledge of containers.

Does that make sense?

from community-protocols.

robrez avatar robrez commented on May 30, 2024 1

I've been interested in an approach like this since watching Justin's talk on DI via events some years back. Really love the way things are shaping up

One of the ideas I was eventually wanting to try out was to dispatch an event allowing a request for multiple values at once. Modifying Ben's example a bit..

  this.dispatchEvent(
    new ContextEvent({
      'cool-thing': {
        callback: coolThing => {
          this.myCoolThing = coolThing; // do something with value
        },
      },
      'another-thing': {
        callback: anotherThing => {
          this.anotherThing = anotherThing;
        },
      },
    })
  );

Possibly a preoptimization, I'm not really sure how costly lots of events in connectedCallback might be...

One potential problem is if two separate ancestors are responsible for providing a given piece of the puzzle. I'm not sure if the community protocol has an opinion around a provider using e.stopPropagation() if you actually run the callback 🤔. I suppose that would generally be a consideration w/ an event requesting just a single value. Multi-values would certainly add some complexity to the situation

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024 1

@robrez interesting idea, I'm a little hesitant to introduce more Event traffic into the mix. They're not entirely free and have a small overhead in object creation. We could achieve the same result without the extra event perhaps, but we would be paying the event creation overhead on every value update. Its likely not a big deal though, so might be worth exploring. I would like a better mechanism for enforcing the 'once' behavior.

from community-protocols.

Westbrook avatar Westbrook commented on May 30, 2024 1

With the recent expansion of AbortControllers to support removing event listeners, maybe there’s something to be found in leveraging those in a once context and possibly more generally to the overall dispose() process.

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024 1

@veikkoeeva I don't think it could be used for 'constructor dependency injection' since construction happens in the DOM for many web component usage scenarios, however, it can indeed be used to create other dependency injection patterns. We internally at Adobe have some usages where we are doing 'property injection'. Which actually is one of the most common usages of the Context API I forsee. I very much like the pattern of components having properties which can be set directly, or which can be provided via Context events being satisfied via the protocol as this pattern seems very versatile.

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024 1

Now that #10 is merged, let's close this and take discussions to individual issues. Thanks @benjamind !

from community-protocols.

renoirb avatar renoirb commented on May 30, 2024 1

Most discussion should now be happening in individual issue tickets filed against the proposal in this repo.

Yes, that's right. I was scrolling around to see currently opened tickets for Context API, and I've found is:issue is:open context in:title

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024 1

And you can open new issues prefixed with [context] if you have a new topic to raise.

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024

@sorvell might be able to put up a draft PR with a few more details relatively soon

from community-protocols.

Westbrook avatar Westbrook commented on May 30, 2024

preact-custom-element is also relying on a dispatched event to resolve context: https://github.com/preactjs/preact-custom-element/blob/master/src/index.js#L71 so definitely some prior art here. One area that I've struggled with a little bit when thinking of similar is the idea of live data without being tightly coupled to a renderer. I look forward to the PR to see how that might be addressed therein!

from community-protocols.

ryansolid avatar ryansolid commented on May 30, 2024

The one thing I've wondered with this event sort of approach is timing. To use events am I correct the element would have to be connected to the DOM? Would the data be available to us at the time of connectedCallback?

The other thing is whether slotting should have any consideration. Can light DOM children slotted under Shadow DOM Provider still get access to context.

With Solid Element I use a bit of a different approach. I do lookups up the DOM tree traversing in and out of Slots and Shadow roots. Once you are connected you are then attached to the context more or less since it's just using the DOM heirarchy. I realize events probably do the same thing without re-creating this walk but as I said I never was comfortable trusting the timing of things. It's possible things have changed from the heavily polyfilled ecosystem we had here a few years ago.

And I had a second motivation where I wanted to unify context with the non-webcomponent side which does everything before being attached (or even connected to it's parent) so I needed some creativity there.

from community-protocols.

LarsDenBakker avatar LarsDenBakker commented on May 30, 2024

When I was implementing our router I created a context API using events. I abandoned the idea in the end for other reasons, but it worked out reliably cross browsers. Lion also uses events for form registration which needs to be synchronous.

from community-protocols.

JosefJezek avatar JosefJezek commented on May 30, 2024

Here is one implementation Polymer/pwa-helpers#64

unistore is waiting for the context developit/unistore#175

from community-protocols.

DeadWisdom avatar DeadWisdom commented on May 30, 2024

The initial event's callback adds the need for an event dispatching mechanism. The provider must act as an observer and has to track consumers (all of the callbacks). Another idea: the provider could simply modify the context event object with the data, a reference to itself, and an event name that it will trigger on itself to update the value. The consumer could then use the data, and then addEventListener() onto the provider, and consume as normal.

Example code

from community-protocols.

jarrodek avatar jarrodek commented on May 30, 2024

Hi,
Too bad I missed this discussion before. This is a problem that was troubling me for some time. After designing 3 ecosystems of WC I came up with a very similar approach to the presented above. There is a significant difference between what I was doing so far and what's in the proposal. I dismissed the idea of using callbacks after few months of testing in favor of using promises. Each event extends CustomEvent and has the mandatory result property defined on the detail object. The result is just a promise returning a value that the context provider produces.
The context request event dispatched from the component describes what kind of context the component is expecting but I use the type instead of the name property. Both have pros and cons. I like using the type property because inside the context provider I can register events for specified types ignoring all other events. It's then easier to understand the context provider is doing by just looking at events registration. It also allow to filter out easily what the provider can listen to. The cons for using this is that a large application can have hundreds of events like this. In a multi-tenant environment (different apps running in the same document) having a single event for all may be considered as a security issue. There's no was to statically analyze that this component coming from this application can request this context.

The proposal also does not specify how to pass arguments to the context provider. I assume this would be passed in the detail object. However, without standardization of this would make it only partially useful as you won't get proper types support. I fixed this by defining a number of events and these events have well defined properties. At least in the declaration file. I tried both defining properties on the event and on the detail object. Both works pretty well but from my experience it's easier to keep it all in the detail object.

If I would to design the architecture for this today I would design a new type of event that extends CustomEvent interface. This event has the result property on the detail object which is a promise resolved by the context provider. The promise is not set automatically when it's created but rather when the context provider handles the event. Missing value (no promise) means that the event was not handled and the component should deal with this somehow.

For an event subscription, when the context provider announces changes in the context, I use separate set of events which I call state events. The context provider dispatches an event when a value change. A component registers event listeners for a specific state change event. When change occurs it requests the changed data from the provider (I usually pass the meta data about the change to this state event containing the identifier of the object, if any, and optional change name).

This way I was able to build rather large applications that work both separately as a standalone application and in a multi-tenant environment. I hope my POV will help understand different use cases and implementations.

from community-protocols.

Westbrook avatar Westbrook commented on May 30, 2024

I dismissed the idea of using callbacks after few months of testing in favor of using promises.

@jarrodek could you give a little more information as to what you found lacking in callbacks during your test period?

For an event subscription, when the context provider announces changes in the context, I use separate set of events which I call state events. The context provider dispatches an event when a value change. A component registers event listeners for a specific state change event. When change occurs it requests the changed data from the provider (I usually pass the meta data about the change to this state event containing the identifier of the object, if any, and optional change name).

I'd love to see this fleshed out a little bit more in code. I'm not sure where the binding responsibilities and patterns for the "state events" occur. It feels like you'd be adding a good amount of decentralized responsibility to the consumer of the context, which might be beneficial for its disconnection from the context generally but feels like it might actually end up being a lot more work that just registering a callback centrally on the context provider.

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024

@benjamind this looks really great. Can't wait to discuss tomorrow!

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024

btw, @jarrodek there's really no reason to use CustomEvent for these protocols. We can always define an interface that extends Event instead.

from community-protocols.

jarrodek avatar jarrodek commented on May 30, 2024

Sorry for not responding faster.

@Westbrook @benjamind Callbacks a generally OK but as it tuns out they need additional logic around them. While with what I have done I can use an async function and relatively simple logic to read the state from the store, I have to do a bit more when working with callbacks. Consider this. The component requests an information from a context provider that is a IDB wrapper connected to the events system. The component renders a specific view for the data. I setup the component in the DOM with the id of the information this component should render.

Inside this component I can build a single async function that refreshes the state via dispatching the event. Something similar to the following:

set dbId(value) {
  this.#id = value;
  this.update();
}

async update() {
  const { dbId } = this;
  const e = new CustomEvent('getcustomer', {
    bubbles: true,
    cancelable: true,
    composed: true,
    detail: {
      id: dbId,
    }
  });
  this.dispatchEvent(e);
  const customer = await e.detail.result;
  // do stuff with the customer 
}

In my ecosystem I have a library of such events with types definitions so I can simplify this even further:

set dbId(value) {
  this.#id = value;
  this.update();
}

async update() {
  const { dbId } = this;
  const customer = await AppEvents.Customer.get(this, id);
  // do stuff with the customer 
}

With callbacks I can achieve the same goal but I would need at least two functions

update() {
  const e = new ContextEvent('getcustomer', this.customerCallback.bind(this));
  this.dispatchEvent(e);
}

customerCallback(customer) {
  // do stuff with the customer 
}

The effect is the same but I believe the ergonomics of this may be optimized.

from community-protocols.

jarrodek avatar jarrodek commented on May 30, 2024

@justinfagnani I completely agree. At some point I started experimenting with having own events extending the Event interface and adding own properties to it. Lot of work but end effect is that I have typed events (via t.ds files) that are easy to use. However, seeing that standardized would be so much better that spending hours defining own events.

from community-protocols.

jarrodek avatar jarrodek commented on May 30, 2024

As for an example. This is the library I am using in one of my OSS apps: https://github.com/advanced-rest-client/arc-events/tree/stage/src/telemetry
In the Events.js I define custom events the application is using. This is an overkill the way I did it, but it's more of a proof of concept at this point.
Then in the context provider I execute an action. It's not really retuning a value (these are reporting events) but it could.
Anyway, I just wanted to give you a different POV on this. Hope this helps.

from community-protocols.

MikeVaz avatar MikeVaz commented on May 30, 2024

I just use similar pattern in one of our projects. But I am actually attaching resolve and reject from a promise to the event detail. Just discovered this thread though.

   /**
     * Request data using an event
     * @param {?String} method - Data request method
     * @param {?String} name - Data source name
     * @param {?Object} params - Request parameters
     * @returns Promise
     */
    requestData(method, name, params) {
      const promise = new Promise((resolve, reject) => {
        this.dispatchEvent(
          new CustomEvent(`somename`, {
            detail: {
              name: name,
              method: method,
              params: params,
              resolve: resolve,
              reject: reject,
            },
            bubbles: true,
            composed: true,
          })
        );
      });
      return promise;
    }

Promises seems to be more flexible comparing with callbacks.

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024

@MikeVaz I initially went down a similar path, but I realized one restriction of the promise attached to event model is that the promise can only be resolved once. For cases like wanting to request a theme from higher up the hierarchy and be able to respond to external theme changes this doesn't work so well. I also toyed with just attaching the payload to the detail in the event with the emitter reading it back off after firing the event. This has the same problem of only being usable once. We want I think to go for the 'most capable approach' with this API so it feels like allowing multiple delivery of requested data is valuable.

@robrez this approach also came up in internal discussions at Adobe. I like this extension a lot and will likely include this in the proposal PR I hope to put up later this week/early next. I also want to detail some approaches I have considered for handling context name conflicts by having context providers which handle aliasing contexts, as well as the possibility of caching context providers that was discussed at the last meeting on this topic.

I hope to get the proposal PR up more formally end of this week to detail the main proposal and outline some of these extensions so we can have something to more formally review and hone. Apologies for not getting to it sooner.

from community-protocols.

MikeVaz avatar MikeVaz commented on May 30, 2024

but I realized one restriction of the promise attached to event model is that the promise can only be resolved once. For cases like wanting to request a theme from higher up the hierarchy and be able to respond to external theme changes this doesn't work so well.

You kind of want to have a single source of truth at the Datasource / ContextProvider level. Where we resolve the promise. Why would we want to resolve it twice? In our implementation we also add a method parameter which can be used to detect if you need to change something rather than just requesting data.

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024

If you only resolve the requested data once, how would an element become aware of a change in the context value?

In the case of a theme provided from somewhere further up in the tree, if the theme is changed, how would the component be notified? With single resolution there's no way, unless we pass something instead of the value which in turn provides the value on a subscription basis. This would then require more book-keeping on the part of the consumer to properly cleanup these subscriptions with the lifecycle of the element.

I feel like we're better off keeping this API as open as possible, rather than being pre-emptively restrictive and thus putting more burden on users of the API to define further specifications for how to interact with context values which change.

If we compare the proposal here with the React Context API that too allows multiple resolution of the value:

All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.

I believe this ability has value, it means that component authors won't necessarily have to build up more subscriptions and do more handling for simple cases where we have a context value that is changing through the lifetime of the requesting component.

I understand traditionally in a Dependency Injection framework some have a single resolution principle, but this is not a DI framework. You could build one atop this protocol, but that is not the main goal here, this is really about allowing shortcuts for data delivery to eliminate prop-drilling.

from community-protocols.

robrez avatar robrez commented on May 30, 2024

Reading through the draft proposal again, particularly the once option

It reminded me of addEventListener(..., ..., { once: true }) which kinda led me down an oddball train of thought...

The provider could dispatch an event to the consumer; and the consumer could add an event listener to itself -- perhaps using the "once" option to enforce that requirement where it exists.

Seems overcomplicated and probably misses the mark on some of the goals... hesitant to even mention it but 🤷

from community-protocols.

mihar-22 avatar mihar-22 commented on May 30, 2024

I'm excited about this proposal!

It's critical for me in that I'm designing a solution to extract media state from a media provider (eg: <video>), and passing said state down the DOM to multiple UI component consumers. I've worked on various solutions around this and happy to chime in.

Probably goes without saying but a standard is really needed around this, so I appreciate the effort by the community 😄

To summarise my thoughts: I think events and callback/s is the right way to implement this feature but it's an internal detail of the context provider/consumer discovery. It's not something that should be implemented by developers at the web component design level. I can't see the point of decoupling them at that level via events, and why anyone would want to deal with naming collisions/conventions, difficulty typing the context callback and a verbose means of wiring everything up. These are just my thoughts... feel free to let me know I'm wrong or what I'm missing.

I guess I don't understand in what "context" (pun?) we're talking about this implementation. As a standard? Feature of Lit? Best practices for library authors? Or...?

Current Proposal

I was reading the proposal by @benjamind (thanks for your time/work thus far 👏 ), and I was just wondering what's the point of decoupling the context consumer and provider. I understand that it achieves a lower-level implementation that we can layer on top of, but I can't seem to think of why or where it would be required. This feels more like a DI solution than a focus purely on a context solution (prop drilling).

I think your proposal actually highlights the disadvantages of decoupling well but doesn't highlight advantages really. I see this section also aims to address the concerns around a createContext implementation in which we couple the consumer and provider:

For web components this creates a centralisation issue. If all components want is a logger context, where would the logger context object be defined? Who owns that module? Will all consumers agree to depend upon it?

It's a little hand-wavy and vague to me... I'm still not seeing an issue. I don't see any advantages in introducing a namespace for the context API which will obviously lead to collisions. In the logger example what's so hard about exporting the context from the same module where the Logger class is declared? What's a real-world example of where consumers can't agree on depending on the same module? I'm not denying it's possible existence but just curious if it's really a problem.

In the current proposal, I believe most developers will either resort to extending this solution with a createContext like API OR maintaining a central module of context keys.

I'm also not a big fan of the idea of playing around with events to connect element/context... feels verbose, error prone and unnecessary. Simply the uncertainty of the type of object you're going to receive is bad sign to me (due to naming collisions). We can bandaid this with naming conventions but why if we can design a solution that avoids it?

Design Thoughts

  • If we're purely talking about prop drilling then in my opinion both a provider and consumer naturally map to component state or a property. I think from an ergonomic point of view we have to design this solution to work with plain JS similar to the way properties are declared on the LitElement class.
  • Ergonomics again -> It'd be ideal to support decorators for both TS users and future JS decorator support.
  • I strongly believe that the consumer and provider should not be decoupled. They are tightly bound for a good reason and I believe React got this part right.
  • The context lifecycle should try it's best to mirror the native HTMLElement lifecycle and we should avoid introducing new terminology or API.
  • Both consume and provide should receive an options object which we can use to modify behaviour (eg: requesting a context value once) OR to tap into the lifecycle (ie: for updating renderer or cleanup).

Here's a sample and very rough API of what I'm thinking...

Definitions

export type ContextHost = HTMLElement;

export type ContextConsumerDeclaration = Context<any> | {
  context: Context<any>,
  // ...options
};

export interface ContextConsumerDeclarations {
  readonly [key: string]: ContextConsumerDeclaration;
}

export type ContextProviderDeclaration = Context<any> | {
  context: Context<any>,
  // ...options
}

export interface ContextProviderDeclarations {
  readonly [key: string]: ContextProviderDeclaration;
}

export interface ContextHostConstructor {
  new (...args: any[]): ContextHost;
  readonly contextConsumers?: ContextConsumerDeclarations;
  readonly contextProviders?: ContextProviderDeclarations;
}

export interface ContextProvider<T> {
  value: T;
  // Better name?
  reset(): void;
}

export interface ContextConsumer<T> {
  readonly value: T;
}

export interface ContextLifecycle<T> {
  onConnected?(): void;
  // Ability to prevent update by returning false?.
  onUpdate?(newValue: T): boolean;
  onUpdated?(newValue: T): void;
  onDisconnected?(): void;
}

export interface ContextProviderOptions<T> extends ContextLifecycle<T> {}

export interface ContextConsumerOptions<T> extends ContextLifecycle<T> {
  once?: boolean;
  // Not sure but someway to transform the consumed context would be handy.
  transform<R>?(newValue: T): R
}

export interface Context<T> {
  initialValue: T;
  provide(host: ContextHost, options?: ContextProviderOptions<T>): ContextProvider<T>;
  consume(host: ContextHost, options?: ContextConsumerOptions<T>): ContextConsumer<T>;
}

API

function createContext<T>(initialValue: T): Context<T> {
  // ...
}
// Decorators

function consumeContext<T extends Context<any>>(
  context: T, 
  options?: ContextConsumerOptions
): PropertyDecorator {
  // ...
}

function provideContext<T extends Context<any>>(
  context: T, 
  options?: ContextProviderOptions
): PropertyDecorator {
  // ...
}
// Mixin to introduce the context API support. Ideally used on your base library element.
function WithContext<T extends ContextHostConstructor>(Base: T): T {
  // ...
}

Usage

JavaScript

const context = createContext(10);

class MyProviderElement extends WithContext(HTMLElement) {
  constructor() {
    super();
    this.context = context.initialValue;
  }

  /** @type {ContextProviderDeclarations} */
  static get contextProviders() {
    return {
      context,

      // OR
      context: {
         context,
         // ...options
      }
    };
  }
}

class MyConsumerElement extends WithContext(HTMLElement) {
  constructor() {
    super();
    this.context = context.initialValue;
  }

  /** @type {ContextConsumerDeclarations} */
  static get contextConsumers() {
    return {
      context,

      // OR
      context: {
         context,
         // ...options
      }
    };
  }
}

TypeScript or with TC39 Decorators

const context = createContext(10);

class MyProviderElement extends WithContext(HTMLElement) {
  @provideContext(context, { /** options */  }) context = context.initialValue;
}

class MyConsumerElement extends WithContext(HTMLElement) {
  @consumeContext(context, { /** options */  }) context = context.initialValue;
}

from community-protocols.

UpperCod avatar UpperCod commented on May 30, 2024

Hi, I share with you @atomico/channel, it solves the following objectives:

  1. read the context of parent
  2. modify the context for children

Example

import { Channel } from "@atomico/channel";

const CHANNEL = "MyChannel";
// Parent channel
const parentChannel = new Channel(document.body, CHANNEL);

class MyComponent extends HTMLElement {
  constructor() {
    super();
    // Child channel
    this.channel = new Channel(this, CHANNEL);
  }
  connectedcallback() {
    this.channel.connected((data) => (this.textContent = JSON.stringify(data)));
  }
  disconnectedCallback() {
    this.channel.disconnect();
  }
}

// Connect the channel to the native DOM event system
parentChannel.connect();

parentChannel.cast("I'm your father");

The api is minimalist.

channel.connect() : create connection and subscription.
channel.cast(): allows creating a new state to inherit to children
channel.disconnect(): clean connection and subscription

Implementation example https://webcomponents.dev/edit/9X95yAWPKW0mdAg7OYZd

from community-protocols.

justinfagnani avatar justinfagnani commented on May 30, 2024

@mihar-22 one of the main goals of this proposal is decoupling via events, it's a core principle. Not only might providers and consumers not be directly aware of each other (ex: loggers, theming, etc.) but with web components the components might not share any implementation. Events already provide the cross-library, cross-component communication that we need for this.

The reason why this protocol is defined in terms of events is because that's the underlying mechanism. Libraries can implement the protocol and provide whatever nice interface into the system that they want, as implemented by @benjamind in lit/lit#1955.

The interfaces you have there might be ok, but at the protocol level we need to define how consumers and providers actually communicate. That's the event protocol, and once we have this, implementations can freely use any interfaces they want, include ones like yours.

Regarding createContext, I prefer that over string-based keys, we're having a discussion in the PR here: https://github.com/webcomponents/community-protocols/pull/10/files#r622357606

from community-protocols.

veikkoeeva avatar veikkoeeva commented on May 30, 2024

Can this be used to implement constructor dependency injection? I'm not fluent enough to read the proposal or infer from the information, but a dependency injection scenario would be interesting to me. One good article on using React State API for constructor dependency injection, via Locator pattern, is at https://blog.testdouble.com/posts/2021-03-19-react-context-for-dependency-injection-not-state/ as far as I could find.

Now with reactive controllers, this injecting a HttpClient as described in the article to a repository and that repository to reactive controllers (and handling state in SQL database in browser) would look an interesting pattern to me at least.

from community-protocols.

renoirb avatar renoirb commented on May 30, 2024

/me looking where to watch the discussion online on that API. (oh, right is:issue is:open context in:title)

from community-protocols.

benjamind avatar benjamind commented on May 30, 2024

Most discussion should now be happening in individual issue tickets filed against the proposal in this repo.

This way people can make PRs for changes to the proposed API and we can iterate from there. It's also fine to just open issue tickets for points of discussion if there are concerns that you don't immediately have solutions for.

from community-protocols.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.