kabinet
TypeScript icon, indicating that this package has built-in type declarations

1.4.0 • Public • Published

🗄️ kabinet

Kabinet is a minimal framework for (p)react based webapps that provides an external state designed to be used together with with React's useEffect and useSyncExternalStore.

See Subscribing to an external store in the React manual for a better explanation why this is useful.

Changes since 1.x

From version 1.x the API is strictly typescript and compiled to ES6.

Installation

npm install kabinet --save-dev

Usage

Example using useSyncExternalStore with a custom hook

This example draws from the useSyncExternalStore example.

count.ts

import Store from "kabinet";
import { useSyncExternalStore } from 'react';

type CountState = { count: number };

class CountStore extends Store<CountState>{
    increment() {
        this.setState({ count: this.state.count + 1 });
    }
};

export const countStore = new CountStore({ count: 0 });

export function useCountStore() {
  return useSyncExternalStore(
    subscribe => countStore.observe(subscribe), 
    () => countStore.getState()
  );
}

App.tsx

import { useCountStore, countStore  } from './count';

function App() {
  const {count} = useCountStore();

  return (
    <button onClick={() => countStore.increment()}>
      count is {count}
    </button>
  )
}

export default App

Background

Since reading about the flux architecture pattern I've had copies of something like store.js around in my projects. Kabinet is where I publish my latest copy. Since my teams have moved to typescript, I dropped runtime type systems and have simplified this library significantly.

kabinet is simple, uses no globals, and can be made to work well with server-side rendering.

SSR (Server Side Rendering)

Examples above used singletons, which which should be avoided (or used with care!) when rendering server-side.

One simple solution is is to keep track of all singletons and make sure to clean and swap them out before each render using a "store keeper".

See ./src/keeper/ for a small proof of concept:

server.ts

import { setStore, clearStores } from "kabinet/dist/keeper";
import { CountStore } from "./client/stores/count";

const render = (App, count=0) => {
  clearStores();
  setStore(CountStore, new CountStore({count}));

  return ReactDOMServer.renderToSTring(<App />);
}

App.tsx

import { CountStore  } from './count';
import { getStore } from "kabinet/dist/keeper";

const countStore = getStore(CountStore);

const useCountStore = () => {
  return useSyncExternalStore(
    subscribe => countStore.observe(subscribe), 
    () => countStore.getState()
  );
}

function App() {
  const {count} = useCountStore();

  return (
    <button onClick={() => countStore.increment()}>
      count is {count}
    </button>
  )
}

export default App

Demo app

Prepare and run the demo using npm run demo.

Package Sidebar

Install

npm i kabinet

Weekly Downloads

0

Version

1.4.0

License

MIT

Unpacked Size

15.1 kB

Total Files

15

Last publish

Collaborators

  • mvhenten