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

@pago/reactive

v0.2.0

Published

[API Docs](./docs/api/reactive.md) | [CodeSandbox](https://codesandbox.io/s/pagoreactive-playground-zx34h) | [Next.js Example](./examples/nextjs/) | [Examples](./stories)

Downloads

128

Readme

@pago/reactive

API Docs | CodeSandbox | Next.js Example | Examples

You are using React or Preact but find yourself frustrated by continuous bugs, errors or ceremony caused by the Hooks API? You thought you could avoid using a separate state management library like Redux, Recoil or MobX but started to run into unexpected performance issues with the Context API?

Then this library will eventually be the one for you! A reactive component model on top of React and Preact with automatic performance optimizations and a simple and predictable API that gets out of your way and supports you in achieving your goals. Blatantly copied from the fantastic Vue Composition API. But for React / Preact.

Huh? Eventually? Oh yes, this thing is bleeding cutting edge and likely to cause you all kinds of pain right now. Please don't use this in production. We are looking for feedback and observations from experiments you run. We fully expect to change major parts of the API in various different ways while we try to find the right set of primitives and abstractions to have a good balance between power and ease of learning.

If you would like to play around with the library:

Project Plan

We are roughly following planning to go through the following steps:

  • [x] Make it work
  • [ ] Make it good (<-- we are here)
  • [ ] Stable release
  • [ ] Make it fast
  • [ ] Make it small

Current State of the Project

  • [x] Works with Preact & React
  • [x] Very little boilerplate on top of React (JS: none, TS: minimal r)
  • [x] Observable values
  • [x] Efficient derived values
  • [x] Works with Suspense
  • [x] Works with React.Context (through inject)
  • [x] Concurrent Mode Safe (!) (as far as I can see, Expert review would be great)
  • [x] Reuse your existing Hooks in a Reactive Component through fromHook
  • [x] Reuse ref values in Hooks components through useRefValue
  • [x] Doesn't show any wrapper components in React DevTools
  • [x] Perfect for incremental adoption into existing projects (use the pragma comment for per-file adoption)
  • [ ] TypeScript: Do we really need r? Can we adapt the JSX.Element['type'] property to include our kind of components?
  • [ ] Lifecycle callbacks (do we really need them? All can be replicated in user-land if needed)
  • [ ] Rx.js interop? Useful? How do we handle subscriptions?
  • [ ] Optimized Preact implementation (by tapping into its plugin API)
  • [ ] Documentation
  • [ ] Consistent naming of things (so far copied Vue API for a lot of things - do the names match & make sense in this context?)
  • [ ] Optimization (Performance & Code Size)

Examples

A Counter component

/** @jsxImportSource @pago/reactive */
import { ref } from '@pago/reactive';

function Counter(props) {
  const count = ref(0);

  return () => (
    <div>
      <div>Count: {count.current}</div>
      <div>
        <button type="button" onClick={() => (count.current += props.step)}>
          Increment
        </button>
        <button type="button" onClick={() => (count.current -= props.step)}>
          Decrement
        </button>
      </div>
    </div>
  );
}

A Timer component

/** @jsxImportSource @pago/reactive */
import { r, ref, effect } from '@pago/reactive';

interface Props {
  step: number;
  delay: number;
}

function Timer(props: Props) {
  const count = ref(0);

  effect(onInvalidate => {
    const timer = setInterval(() => {
      // update is needed because we are reading from and writing to count
      count.update(current => current + props.step);
    }, props.delay);

    onInvalidate(() => clearInterval(timer));
  });

  return r(() => (
    <div>
      <div>Count: {count.current}</div>
    </div>
  ));
}

Setup

The easiest way to setup @pago/reactive for either React or Preact is to leverage the new jsxImportSource option and to set it to @pago/reactive.

Requirements:

  • React 17 or later
  • or Preact (todo: insert correct version)
  • Babel (todo: insert correct version)
  • or TypeScript (todo: insert correct version)

Per file

Specifying @pago/reactive as the JSX factory can be done using a comment at the beginning of the file. This should be supported by Babel & TypeScript.

/** @jsxImportSource @pago/reactive */

Babel

As specified in the babel documentation:

{
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "runtime": "automatic",
        "importSource": "@pago/reactive"
      }
    ]
  ]
}

Q & A

Is this ready for production?

Not yet.

Why ref().current instead of ref().value?

Because it allows us to do this:

import { ref, effect } from '@pago/reactive';

function CounterComponent() {
  const el = ref();
  effect(function updateDOMManually() {
    el.current.innerHTML = 'Hello World';
  });
  return () => <div ref={el}></div>;
}

Why does TypeScript complain about components not being components?

When you try to use a component like the one below with TypeScript in JSX, it'll inform you that () => Element is not a valid type for a JSX Element.

import { ref, effect } from '@pago/reactive';

function CounterComponent() {
  const el = ref();
  effect(function updateDOMManually() {
    el.current.innerHTML = 'Hello World';
  });
  return () => <div ref={el}></div>;
}

For the time being we don't have a better solution than to use the provided r function, which is basically a type cast that fakes the right type to make TypeScript happy.

import { r, ref, observe } from '@pago/reactive';

function CounterComponent() {
  const el = ref();
  observe(function updateDOMManually() {
    // `observe` is currently invoked immediately, rather than at the next tick
    // not sure if that behaviour is better or worse than delaying it a bit
    if (!el.current) return;
    el.current.innerHTML = 'Hello World';
  });
  return r(() => <div ref={el}></div>);
}

An alternative would be to use the wrap function explicitly.

import { wrap, ref, effect } from '@pago/reactive';
const CounterComponent = wrap(function CounterComponent() {
  const el = ref();
  effect(function updateDOMManually() {
    el.current.innerHTML = 'Hello World';
  });
  return () => <div ref={el}></div>;
});