Skip to content

Add channel based signals #44

@denisboreyko97

Description

@denisboreyko97

Here is the functionality with detailed comment that I would add to signals library. It solves the problem when I want to connect separate components on the page through signals, but I do not want one component to update all instances of another one which is dependent through signals. instead I would create channel based signal and would change signal for specific channel, to update only specific instances of dependent components

/**
 * Initializes a channel-based signal manager that enables isolated reactive state
 * management across multiple named channels. Each channel holds an independent signal,
 * allowing decoupled control for components sharing common logic.
 *
 * Example usage:
 * const mySignal = $channelSignal();
 * mySignal.setForChannel('PageChannel1', true);
 * const current = mySignal.forChannel('PageChannel1');
 *
 * This is particularly useful for generic components—like pagination—that rely on reactive state.
 * Consider a scenario where multiple paginated tables are rendered on the same page:
 * 
 * With a single signal:
 * const pageNumber = $signal(1);
 * 
 * Every pagination instance would trigger updates across all tables via a shared effect:
 * $effect(() => {
 *     if (pageNumber.value !== this.pageNumber) {
 *         this.pageNumber = pageNumber.value;
 *     }
 *     this.fetchCurrentPage();
 * });
 *
 * Replacing it with a channel-based signal isolates each table’s pagination:
 * const pageNumber = $channelSignal();
 * 
 * Each Pagination component assigns its own channel:
 * 
 * export default class Pagination {
 *     @api channel;
 *     
 *     setPage(event) {
 *         pageNumber.setForChannel(this.channel, event.detail.value);
 *     }
 * }
 * 
 * export default class GenericTable {
 *     @api channel;
 *     pageNumber;
 * 
 *     connectedCallback() {
 *         $effect(() => {
 *             this.pageNumber = pageNumber.forChannel(this.channel).value;
 *             this.fetchCurrentPage();
 *         });
 *     }
 * }
 *
 * This approach prevents unwanted cross-updates and keeps component state scoped to its context.
 *
 * @function
 * @returns {Object} An object containing `forChannel` and `setForChannel` methods
 *                   for managing per-channel signals.
 *
 * @property {Function} forChannel - Retrieves or initializes a signal for a given channel.
 * @param {string} channel - The channel identifier.
 * @param {*} [defaultValue] - Initial value if the signal is newly created.
 * @param {Object} [options] - Optional signal configuration.
 * @returns {*} The signal linked to the provided channel.
 *
 * @property {Function} setForChannel - Updates the signal value for a specified channel.
 * @param {string} channel - The target channel name.
 * @param {*} value - New value to assign.
 * @param {Object} [options] - Optional signal configuration.
 */
function $channelSignal() {
  return {
    forChannel: function(channel, defaultValue, options) {
      if (!this[channel]) {
        this[channel] = $signal(defaultValue ?? undefined, options);
      }

      return this[channel];
    },
    setForChannel: function(channel, value, options) {
      if (!this[channel]) {
        this[channel] = $signal(value, options);
      } else {
        this[channel].value = value;
      }
    }
  };
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions