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

@kamyil/reactive-function

v1.5.4

Published

The one simple function that allows you to make your values reactive to each other!

Downloads

22

Readme

Reactive Function

The one simple function that allows you to make your values reactive to each other!

NPM https://www.npmjs.com/package/@kamyil/reactive-function Link to NPM Package

DEMO: https://stackblitz.com/edit/typescript-maqe1v?file=index.ts

Advantages

  • Minimalistic
  • Very Light-weight
  • 100% Hardly Typed
  • Framework agnostic
  • Zero dependencies

(DISCLAIMER: This package is mainly my side project created and maintained purely for fun. It is battle tested and can be used on production freely, but... please. Keep in mind that this project can be, but should not be used as an default reactive system if you're planning to create serious business-valuable project for either yourself or company you work for, since there are various of JavaScript frameworks already that are way more advanced. Also if you're looking for simpler but framework-agnostic reactivity system, I would rather recommend @vue/reactivity (https://www.google.com/search?client=safari&rls=en&q=%40vue%2Freactivity&ie=UTF-8&oe=UTF-8). Consider this library being a last-ditch alternative)

OFC: feel free to suggest any changes in pull requests or raise an issue in Issues tab 😅

How to use it?

First import the function (since it's written in TypeScript, you can auto-import it);

import { reactive } from '@kamyil/reactive-function';

if you receive the error from your bundler/compiler that it cannot get the module (f.e. Vite can do that, because it points automatically on types.d.ts file instead of index.ts file), then import it this way

import { reactive } from '@kamyil/reactive-function/index';

or you can destructure it using require if you do not use any kind of module bundler for writing your Node application

const { reactive } = require('@kamyil/reactive-function');
// or
const { reactive } = require('@kamyil/reactive-function/index');

Then pass your value into reactive function and assign it's result to variable

import { reactive } from '@kamyil/reactive-function';

const myReactiveValue = reactive('some random string');

And from now on you can get always-freshly updated value by accessing it's value property

or update it by mutating value property as well

import { reactive } from '@kamyil/reactive-function';

const myReactiveValue = reactive('some random string');

console.log(myReactiveValue.value); // => 'some random string'
myReactiveValue.value = 'some another string';
console.log(myReactiveValue.value); // => 'some another string'

What's so reactive about that huh? It's obvious that if you mutate properties, you will update and retrieve only fresh values

You're absolutely right! But now the magic is going to happen. Let's say: now you want to make it reactive to other reactive value

import { reactive } from '@kamyil/reactive-function';

const myNumber = reactive(2);
// and now let's make it dependent from number above
const anotherNumber = reactive(() => myNumber.value * 2);

// and now let's check if it will react on first reactive value change
myNumber.value = 4;

console.log(myNumber.value); // => 4
console.log(anotherNumber.value); // => 8

Voila!, it does react. But you may ask 'Why there is a function in second reactive variable?'

The answer is actually pretty simple: JavaScript unlike Lazy computed languages like Haskell, it does compute expressions early before any function calls. So this kind of expression

const myNumber = reactive(2);
const anotherNumber = reactive(myNumber.value * 2);

... will early and automatically compute myNumber.value * 2 into simple 4 without saving any reference to myNumber variable. Thanks to passing function, we can save all references to all used variables in that function and then use those variables to compute always fresh and updated value as a result.

How does it work?

All of your reactive values live inside $reactiveDataContainer variable that sits inside window or global object (depending if you're running this function in a browser or Node.js) and those reactive values retrieve, compute and save references in there. When .value is called, then the getter function runs

  • checks all dependencies of itself
  • gets values from them
  • and computes new value

However when f.e. the someReactive.value = 'some new value' is being called, then it runs the setter function that will update the value, update computing function and also trigger/call other dependent values to react and update theirselves. All thanks to JavaScript Proxy API https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy that allows to override default JavaScript behaviour to achieve such a thing 👏

What about objects and arrays?

They can also be used in the same way like primitive values

let testArray = reactive([1, 2, 3]);
let testObject = reactive({
  name: 'Kamil',
  age: 23,
});

However, if you want to update it YOU CANNOT MUTATE IT DIRECTLY. You have to always pass new values to them, because otherwise you will loose all of previous used data

testArray.value = [...testArray.value, 4];
testObject.value = {
  ...testObject.value,
  favoriteHero: 'Daredevil',
};

How to track changes?

For this case there is a special tool function called trackChanges. You can use it to perform your actions on every reactive value change

import { reactive, trackChanges } from '@kamyil/reactive-function';

const testNumber1 = reactive(1);

trackChanges(testNumber1, ({ previousValue, newValue }) => {
  // here will be value before update
  console.log(previousValue); // => 1
  // here will be value after update
  console.log(nextValue); // => 2
});

testNumber.value = 2;
  • first argument: reactive value to track
  • second argument: callback to perform on every change

How to stop tracking changes?

However if you want to stop tracking changes, you can import another tool function called stopTracking. When called, it will stop tracking changes, not perform any passed callback into trackChanges function anymore, and if any callback for stopTracking function was passed - it will call it once.

import {
  reactive,
  trackChanges,
  stopTracking,
} from '@kamyil/reactive-function';

const birthday = reactive(1);

trackChanges(testNumber1, ({ newValue }) => {
  if (newValue === 18) {
    stopTracking(testNumber1, () =>
      alert(`Congratulations! You're an adult now!`)
    );
  }
});

setInterval(() => birthday.value++, 1000);

How to sync reactive variables with HTML?

There is a dedicated tool function for that case called syncWithHTML

import { reactive, syncWithHTML } from '@kamyil/reactive-function';

const myNumber = reactive(1);
const doubledNumber = reactive(() => myNumber.value * 2);

// And it will automatically reflect changes into your DOM Element
syncWithHTML(doubledNumber, '.element-to-sync');

However, if you want more flexibility with auto-updating HTML, then folow this recommendation.

As mentioned before, you can grab always fresh value by getting value property of your reactive variable like so: yourVariable.value. You can combine it with tool function called trackChanges to update the DOM Element your own way. Since trackChanges accepts a callback as a second argument - there you have flexibility to perform any side-effects you want

For TypeScript users

Since this library is heavly using TypeScript and it's glorious features to embrace developer's productivity and hapiness, expect type inference in almost every place possible thanks to TypeScript Generics.

However if you want to type everything manually on your own, you can pass your types/interfaces as a generic to this function

type Person = {
  name: string;
  age: number;
  favoriteHero?: string;
};

testObject = reactive<Person>({
  name: 'Kamyil',
  age: 24,
});

The same goes for tool functions like trackChanges, stopTracking and syncHTML;

When to pass value and when to pass callback?

As mentioned earlier. Pass values directly ONLY if they will not be dependent from any other reactive values. Otherwise, put them after the lambda () => yourVariable.value in order to save reference for your variables and produce always fresh values

I have property $reactiveDataContainer does not exist on type (Window & typeof globalThis) problem

It means that your development environment did not catch extended Window & Global interfaces with this property. The possible fix for that would be adding it manually to your type definition file

import { IReactiveDataContainer } from '@kamyil/reactive-functions';

declare global {
  interface Window {
    $reactiveDataContainer: IReactiveDataContainer;
  }
  namespace NodeJS {
    interface Global {
      $reactiveDataContainer: IReactiveDataContainer;
    }
  }
}

Inspirations

  • @vue/reactivity by Evan You https://www.npmjs.com/package/@vue/reactivity
  • rxjs/observables - https://rxjs-dev.firebaseapp.com/guide/observable