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

reblok

v0.0.28

Published

Tiny state manager for React. It is tiny but powerful

Downloads

32

Readme

reblok

Tiny state manager for React. It is tiny but powerful

Installation

NPM

npm i remos --save

YARN

yarn add remos

Live Demos

https://codesandbox.io/s/reblok-demo-5ksi6t

Recipes

Counter App (3 lines of code)

import { blok } from "reblok";
// creating a blok with initial data
const counter = blok(0);
// using use() method to bind the blok to React component
const App = () => <h1 onClick={() => counter.data++}>{counter.use()}</h1>;

Async data

import { blok } from "reblok";
// block data can be promise object
const userProfile = blok(
  fetch("https://jsonplaceholder.typicode.com/users/1").then((res) =>
    res.json()
  )
);
console.log(userProfile.loading); // true

const UserProfile = () => {
  // if the blok is still loading, a promise object will be thrown
  // and Suspense component will handle loading status
  const { username } = userProfile.use();
  return <div>{username}</div>;
};

const App = () => (
  // blok also supports suspense and error boundary
  <Suspense fallback="Loading...">
    <UserProfile />
  </Suspense>
);

Updating blok using reducer

import { blok } from "reblok";

const counter = blok(0);
counter.set((prev) => prev + 1);

Batch updating

import { batch } from "reblok";

batch(() => {
  counter.data++;
  counter.data++;
  counter.data++;
});
// the counter change triggers once

API Reference

Docs: https://linq2js.github.io/reblok/

Default Export: blok(initialData): Blok

Default Export: blok(sourceBlok, selector, concurrentMode?): Blok

Create a blok from source blok, the blok data is result of selector. Passing concurrentMode to control blok updating behavior

const counter = blok(0);
const doubledCounter = blok(counter, (x) => x * 2);
// when counter blok changed, the debouncedDoubledCounter does not update immediately, it delays update in 100ms
const debouncedDoubledCounter = blok(counter, (x) => x * 2, debounce(100));

Default Export: blok(mutation): any

Blok Insntance: blok.loading: boolean

Blok Insntance: blok.error: any

Blok Insntance: blok.data: any

Blok Insntance: blok.get(): any

Blok Insntance: blok.set(data): void

Update blok data, if the data is the same of previous one, no change is triggered. The data can be:

  • Any object
  • A function which retrieves previous data and return new data
  • A promise object. When passing promise object as blok data, the blok status becomes loading. When the promise is resolved, the blok uses resolved value as its data. When the promise is rejected, the blok uses rejected reason as its error

Blok Insntance: blok.listen(listener): Function

Register listener to listen blok change event and return unsubscribe function

Blok Insntance: blok.use(): any

Bind the blok to React component and return the blok data

const count = counter.use();

Note: use() handles suspense and error boundary automatically. If the blok is loading, a promise object throws when component is re-rendering. If the blok has an error, the error throws when component is re-rendering.

const CountValue = () => <div>{counter.use()}</div>;
const App = () => (
  <Suspense fallback="Loading...">
    <CountValue />
  </Suspense>
);

Blok Insntance: blok.use(selector, compare?): any

Bind the blok to React component and return selected slice of data. By default, blok uses strict compare function to compare selector result. If the selector result is complex object, you can use shallow compare to optimize rendering

Note: Using use() with selector does not handle suspense and error boundary.

// the component always re-render when the state changed because the selector always returns new object
// prevResult !== nextResult
const { id, username } = profile.use((x) => ({
  id: x.id,
  username: x.username,
}));

import { shallow } from "blok";

const { id, username } = profile.use(
  (x) => ({
    id: x.id,
    username: x.username,
  }),
  // using shallow compare function to optimize re-render
  shallow
);

debounce(ms)

Delay updating in X milliseconds

import { blok, debounce } from "reblok";

function updateCounter() {
  counter.set((prev) => prev + 1, debounce(500));
}

updateCounter(); // counter = 0
updateCounter(); // counter = 0
updateCounter(); // counter = 0
// wait in 600ms
// counter = 1

throttle(ms)

import { blok, throttle } from "reblok";

const counter = blok(0);

function updateCounter() {
  counter.set((prev) => prev + 1, throttle(500));
}

updateCounter(); // counter = 1
updateCounter(); // this updating is skipped
// wait in 600ms
updateCounter(); // counter = 2

Updaing nested data

const userProfile = blok({
  name: "dan",
  fullName: "Dan Abramov",
  address: { street: "123", city: "New York" },
  tags: ["React Developer", "Web Developer", "Senior Developer"],
});

// chaining method calls, but the update notification will run twice
userProfile
  .set("name", "dan (updated)")
  .set("fullname", "Dan Abramov (updated)");

// update notfication runs once
batch(() => {
  userProfile
    .set("name", "dan (updated)")
    .set("fullname", "Dan Abramov (updated)");
})();

const addPostfix = (prev) => prev + " (updated)";

// using updaters
userProfile.set("name", addPostfix).set("fullname", addPostfix);

// update multiple paths at once
userProfile.mset({
  name: addPostfix,
  fullName: addPostfix,
  address: (prev, { clone }) => {
    // clone current address object and change city prop
    clone()["city"] = "Washington";
  },
  tags: (prev, { clone }) => {
    // clone current tag array and push new tag
    clone().push("new tag");
  },
});