DEV Community

Valery Zinchenko
Valery Zinchenko

Posted on

2 1

Introduction to Reactivity

You can look at the Reactivity from different angles

It all starts with an Accessor, which typically can be faced as a Variable

let a = 1
a = a + 1 // Gets and Sets `a` variable
Enter fullscreen mode Exit fullscreen mode

Then you want to add some special meaning, behavior or encapsulate the Accessor

class Accessor<T> {
  private value: T
  constructor(initialValue: T) { this.value = initialValue }

  set(newValue: T) { this.value = newValue }
  get(): T { return this.value }
}
Enter fullscreen mode Exit fullscreen mode

Example

const a = new Accessor(1)
a.set(a.get() + 1)
Enter fullscreen mode Exit fullscreen mode

In this context, "Accessor" is simply a structure that encapsulates a value with explicit getter and setter methods. It can be transformed to many other forms besides Observable one.

Now we want to run some code whenever it receives a new value, this is the point when it's called Reactivity since we want to react to a new value.

At this point it becomes obvious that Accessor is not just an Accessor anymore, it's State since we're talking about it as a structure capable of keeping a value, retrieving and updating it.

class State<T> {
  private value: T
  constructor(initialValue: T) { this.value = initialValue }

  set(newValue: T) { this.value = newValue }
  get(): T { return this.value }
}
Enter fullscreen mode Exit fullscreen mode

Now this seems right (to me). Naming means a lot, it can completely change how you see and understand a code, the same structure can share/extend the same code while being used in different ways. This means a lot for a potential change in future.

Observe

Before we continue with our state, we need a structure that would be handling updates observation.

There are two approaches to it: Event-based and Closure-based, let's dive in to Event-based for simplicity.

In theory, whenever something is updated, we would invoke a dispatch function to notify listeners.

We also need to register a listener for new updates (this is usually a callback function itself).

class Messager<T> {
  private readonly callbacks = new Set<(value: T) => void>()

  dispatch(value: T) {
    this.callbacks.forEach(callback => callback(value))
  }

  subscribe(next: (value: T) => void) {
    this.callbacks.add(next)

    // In reality, sometimes we need to unsubscribe from it,
    // so we're returning such function as per the TC39 Observable Proposal, which may be a new standard
    return {
      unsubscribe: () => this.callbacks.delete(next)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

By merging it to the State or Accessor, it would become Observable State/Accessor

class State<T> {
  private readonly messager = new Messager<T>()

  private value: T
  constructor(initialValue: T) { this.value = initialValue }

  set(newValue: T) {
    this.value = newValue
    this.messager.dispatch(newValue)
  }
  get(): T { return this.value }

  subscribe(next: (value: T) => void) {
    return this.messager.subscribe(next)
  }
}
Enter fullscreen mode Exit fullscreen mode

Example

const state = new State(123)
state.get() // 123
state.subscribe(value => console.log("value", value))
state.set(6) // value 6
state.get() // 6
Enter fullscreen mode Exit fullscreen mode

As you can see, this is a very simple though very powerful code, which can be applied widely and extended as well.

You can find a working code in this repository
https://github.com/FrameMuse/event-signal

It's written in TypeScript, to play with it, use TypeScript Playground


If you're writing your own Signal-like structures, make sure to stick to standards, so your code can be easily understood.

Top comments (1)

Collapse
 
framemuse profile image
Valery Zinchenko β€’

I wasn't sure which approach I should take to explain the Reactivity and signals. Let me know if you think I missed something or explained insufficiently.

Visualizing Promises and Async/Await 🀯

async await

Learn the ins and outs of Promises and Async/Await!

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay