10

What does it mean that Solid.js is truly reactive?

 2 years ago
source link: https://typeofnan.dev/what-does-it-mean-that-solid-js-is-truly-reactive/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

What does it mean that Solid.js is truly reactive?

Nick Scialli • May 18, 2022 • 🚀 4 minute read

A question I have been hearing a lot recently is about Solid’s reactivity. Specifically, what makes a framework like Solid more reactive than, well, React.

The answer is the pub/sub model employed by Solid’s signals. Using a pub/sub model, we’re able to write the following code:

const [count, setCount] = createSignal(0);

setTimeout(() => {
  setCount(count() + 1);
}, 1000);

createEffect(() => {
  console.log(count());
});

And we will log the count every time it’s updated (every second). You’ll notice that we don’t have to enumerate our effect’s dependencies—it automatically knows that it should be triggered when count is updated.

How it works

To help understand how reactivity works, we can actually build our own createSignal and createEffect primitives. This will both give us some insight into reactivity and “pull back the curtain” on some perceived framework magic.

To start out with, let’s make createSignal. Based on how we use it, we know it will take an initial value as an argument and return a two-element tuple—a getter and a setter:

function createSignal(initialValue) {
  let value = initialValue;

  function getter() {
    return value;
  }

  function setter(newValue) {
    value = newValue;
  }

  return [getter, setter];
}

To reiterate, we have a createSignal function that takes an initialValue for our signal. It returns a two-element array: the first element is getter, which gets the value, and the second element is setter, which sets the value to newValue.

Next, we need to set up our effect. The trick with effects in this pub/sub model is that createEffect will put the function it calls in the global scope so that the signal can capture it as a listener. That’s a lot to grok, so let me show you what this looks like:

let listener;

function createSignal(initialValue) {
  let value = initialValue;
  const listeners = [];

  function getter() {
    if (listener) {
      listeners.push(listener);
    }

    return value;
  }

  function setter(newValue) {
    value = newValue;
  }

  return [getter, setter];
}

function createEffect(func) {
  listener = func;
  func();
  listener = undefined;
}

So let’s think about what happens when we call the following:

createEffect(() => {
  console.log(count());
});
  1. We have a global listener variable that gets assigned the entire function inside createEffect
  2. That function is actually executed
  3. When that function is executed, the getter for count is called, which adds the listener to the signal’s local listeners array
  4. We set the global listener back to undefined

After our createEffect has been called, any signal called during the initial execution will have captured the effect function as a listener!

The final step is that each signal needs to trigger all of its listeners whenever its value changes. The can be done by looping over the listeners array in the setter function:

let listener;

function createSignal(initialValue) {
  let value = initialValue;
  const listeners = [];

  function getter() {
    if (listener) {
      listeners.push(listener);
    }

    return value;
  }

  function setter(newValue) {
    value = newValue;

    listeners.forEach((listener) => {
      listener();
    });
  }

  return [getter, setter];
}

function createEffect(func) {
  listener = func;
  func();
  listener = undefined;
}

And that makes for a quick and dirty reactive model! We can now see that the initial code we wrote logs the value of count whenever it changes (every second).

const [count, setCount] = createSignal(0);

setTimeout(() => {
  setCount(count() + 1);
}, 1000);

createEffect(() => {
  console.log(count());
});
count being logged every second

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK