npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@reactivjs/streams

v1.1.5

Published

Minimalistic and lightweight reactive objects

Downloads

7

Readme

Streams

Lightweight and minimalistic solution to reactivity. It was originally intended to be used together with ApăJS, but you can always use it as a standalone solution.

Usage

You can npm i apajs-streams or import the functions from https://esm.sh/[email protected].

Reactive objects

Reactive objects can be either primitives or object references. Simply, pass the value you want to make reactive to the stream() function. If a primitive has been provided, its value can be accessed or set via myVar.val. In the case of objects, we can simply access and set the properties as in a plain object (eg: myObject.count):

const count = stream(0);
console.log(count.val); // 0
count.val = 2;
console.log(count.val); // 2

const user = stream({
  username: "Jon",
  email: "[email protected]",
});

console.log(user.username); // 'Jon'
user.username = "Jane";
console.log(user.username); // 'Jane'

Reacting to changes with hook()

The function hook accepts a callback that will be executed whenever any of the dependencies change.

Whenever we read the value of a stream inside a hook, the stream(s) whose value we're reading will be automatically registered as a dependency of the given callback.

Unlike signals*, it's not necessary to provide a way of "peeking" the value of a stream. Effects will only be triggered and registered when passed to a hook.

* Signals are a much more complex project and no way we're even comparing Streams to Signals, this is just an illustrative note

The following is completely safe:

let count = stream(0);
console.log(count.val); // This won't register any side effect!

Usage examples:

const count = stream(0);

hook(() => console.log(count.val));
// Logs 0 when first declared

count.val = 2;
// Automatically logs 2

const user = stream({
  username: "Jon",
  email: "[email protected]",
});

hook(() => console.log(user.username));
// Logs 'Jon'
user.username = "Jane";
// Automatically logs 'Jane'

Computed values:

The simplest way to derive a computed value is by passing a callback to the stream function. Under the hood, it'll be automatically called into a derive which will set the value any time any of the dependencies change.

Notice: Computed streams are still regular streams. This means that you can modify their value from outside the dependency callback. While this is discouraged, nothing is preventing you from doing so.

const num1 = stream(2);
const num2 = stream(1);
const num1PlusNum2 = stream(() => num1.val + num2.val); // 3

hook(() => console.log(num1PlusNum2.val)); // logs 3
num1++; // Automatically logs 4
num2++; // Automatically logs 5
num1PlusNum2.val = 38; // will work but do it at your own risk
num2++; // Logs 6, restoring the original flow

While you cannot create "computed objects", you can easily declare computed properties inside object streams by setting the property to be the computed callback:

let birthYear = 1995;
let currentYear = stream(2023);

let user = stream({
  name: "Jon",
  age: () => currentYear.val - birthYear, // this would be the same as age: stream(() => currentYear.val - birthYear)
});

hook(() => console.log(user.age.val)); // logs 28
currentYear.val++; // logs 29
birthYear++; // doesn't log anything because it's not a reactive variable

Clearing callbacks

Typically, callbacks should be cleared automatically by the garbage collector when the object doesn't exist anymore. However, there are a few situations where you may want to remove the effect from the dependencies manually:

Streams can share state across several files (including components, if you'd like to use it in a frontend application!). If you're creating streams inside the component, these streams should be removed automatically with all their dependencies when the component enters the destroy phase. However, if the stream has been created outside of the component, the callback will remain in the dependencies of the stream even if you have destroyed the component.

Whether it is because of the situation above, or because you simply want to stop a certain callback from happening anymore, know that hooks return the dependency itself. The dependencies (Deps, in the codebase) are simply a class with the callback, a set containing the set(s) where it has been included and an unhook method, which will remove the dependency from the set(s). Example usage:

const count = stream(0);
const effect = hook(() => console.log(count.val)); // logs 0 as normally
count.val++; // logs 1
effect.unhook();
count.val++; // no more logging!

Notice it'll work exactly the same even if we have more than one dependency in the callback

Credits

The following projects and talks served as a profound inspiration to get Streams up and running:

  • Vue reactivity, whose code I tried to understand but I'm not that smart/patient yet hehe.
  • Marc Backes' talk on Vue Conf 2022, which triggered the spark of my curiosity. It also showed me the "capture active strategy" (I'm sure it does have a name, if anyone can share it'd be much appreciated).
  • VanJS, whose minimalistic design and code-saving strategy inspired several decisions on the Streams syntax (both API and codebase).

Upcoming

  • [ ] Type definitions