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 🙏

© 2026 – Pkg Stats / Ryan Hefner

rx-atom

v1.0.0

Published

Atom implementation for ReactiveExtensions.js

Readme

rx-atom

Reactive atom implementation in ReactiveExtensions.js. Uses Ramda compatible lenses to decompose state.

Why

Atoms provide identifier or handler to reference values changing in time. When using immutable values you separate identity and state. Atoms enable to bring those back into one place in sane way.

This implementation of atoms provide reactive interface to observe changes. Method get returns Observable from ReactiveExtensions.js library.

Atom also provides view method, which given Lens returns LensedAtom. It is a bidirectional projection of parent Atom. Meaning that, it gives access only to part of parent Atom (thanks to used Lens) and guarantees consistency with it.

Getting started

import { Atom } from 'rx-atom';

const counter = new Atom(0);

counter.get().subscribe(count => {
  console.log('Current count: ', count);
});
// logs initial value of 0

function inc() {
  counter.modify((count) => count + 1);
}

inc();
// logs modified value of 1

This is simplest usage of Atom. You can create new Atom with initial value. Method get returns Observable of values in this Atom. This observable emits new value on every change of Atom internal value. In this example we just subscribe to this Observable and log it's value to console. In inc function we modify value of Atom using method modify.

Lenses and LensedAtom

import { Atom } from 'rx-atom';
import { propLens } from 'ramda';

const state = new Atom({count: 0, name: ''});

state.get().subscribe(state => {
  console.log('debug state: ', state);
});

function nameWidget(name) {
  name.get().subscribe(name => {
    document.getElementById('name-label').textContent = name;
  });
  
  document.getElementById('name-input').addEventListener('change', function() {
  	name.set(this.value);
  });
}

const nameLens = propLens('name');
nameWidget(state.view(nameLens));

This example show how we can decompose state into smaller chunks and still have consistency and reactive nature. First, we declare global state for whole application. We also subscribe to it to provide logs for debuging. Then we define simple component which accepts Atom with string value. It is just input and text label that are synchronized. When something changes in input field, the label should reflect that change.

We define Lens that picks just name property from object. Then we use it in view method on state Atom. This atom we can pass to nameWidget. Component doesn't know how whole state looks, it gets only value viewed through nameLens (e.g. only name property). Component can modify it's value and this change will be reflected in underlying state Atom. And all subscribers to state Atom will see these changes. Because values flow down and changes start always on top, and because of nature of Observables, whole state is always consistent. If we wan't to introduce circular dependencies, we must explicity add modification of Atom inside subscription, and that's easy to spot and is considered bad practice.

How LensedAtoms works?

Each LensedAtom holds reference to parent Atom and Lens which was used to create it. In that way, Observable from get is just value of parent Atom mapped with Lens. Every modification of LensedAtoms is really modification of parent Atom through Lens This way LensedAtom doesn't really hold any value and is just projection of parent Atom. When calling view on LensedAtom, the resulting LensedAtom holds reference to original parent and Lens is just composition of Lens from parent and itself.

StoredAtom

This library provides also StoredAtom flavour. This Atom's values are stored in LocalStorage on every change, and if value exist in LocalStorage upon Atom creation - it's used as initial value.

import { StoredAtom } from 'rx-atom';

const atom = new StoredAtom('default value', {key: 'local-storage-key'});

This Atom's values will be stored under local-storage-key, and if some value existed here, it will be used instead default value.

Stored atom also accepts options to and from which are used to serialize and deserialize value to LocalStorage. That way it's possible to store only essential part of state.

Prior art

This library is heavly influenced by calmm-js.