<div id="app"></div>
		html {
			font-family: sans-serif;
		}
/*
 * IMPLEMENTATION
 */

// global variable to store the current caller
let caller;

// simple implementation without cleanups and checks for infinite loops
function signal(initialValue) {
  let _value = initialValue;
  const observers = [];
  return {
    get value() {
      if (caller && !observers.includes(caller)) {
        observers.push(caller);
      }
      return _value;
    },
    set value(newValue) {
      if (newValue !== _value) {
        _value = newValue;
        observers.forEach((fn) => fn());
      }
    }
  };
}

function effect(fn) {
  caller = fn;
  fn();
  caller = undefined;
}

function computed(computation) {
  return {
    get value() {
      return computation();
    }
  };
}

// inspired by: https://www.thisdot.co/blog/deep-dive-into-how-signals-work-in-solidjs

/*
 * USAGE
 */

const counter = signal(0);
const double = computed(() => counter.value * 2);

const button = document.createElement("button");
const paragraph = document.createElement("p");
document.body.append(button, paragraph);

button.addEventListener("click", () => counter.value++);

// these effects are hidden by the framework,
// but it's essentially what happens with signals in a component template
const updateButton = () => (button.innerText = counter.value);
effect(updateButton);
effect(() => (paragraph.innerText = double.value));
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.