@chrisyu521/reactive

0.0.12 • Public • Published

@shined/reactive

Unopinionated proxy-based state, with high rendering performance. 🔥

Features

  • 😉 Easy to use: just several simple APIs.
  • ⚡️ High performance: use Proxy to achieve high rendering
  • 🏄‍♂️ Unopinionated: works well in both React and Vanilla JS/TS.
  • 🔐 Secure: snapshot can't be mutated directly.
  • 💻 Redux Devtools Support: partly support Redux Devtools Extension.

Install

# npm
npm i @shined/reactive
# yarn
yarn add @shined/reactive
# pnpm
pnpm add @shined/reactive

Usage

1. Create a store

import { create } from "@shined/reactive";

const store = create({
  name: "Pikachu",
});

// or go with devtools enabled, make sure you've installed `redux-devtools-extension`
const store = create(
  {
    name: "Pikachu",
  },
  {
    devtool: {
      name: "Pikachu Store",
      // ... other options here.
    },
  }
);

Full devtools options see Redux Devtools Extension Documentation.

2. Get snapshot from store

import { store } from "./store";
// import { useSnapshot } from "@shined/reactive";

export default function Foo() {
  const state = store.useSnapshot();

  // or get snapshot by `useSnapshot` hooks
  // const state = useSnapshot(store);

  return (
    <>
      <h1>{state.name}</h1>
    </>
  );
}

3. Mutate the store

You can mutate the state anywhere you like.

For example, mutate it in the same file with the store directly.

NOTE: snapshots are all frozen, so you can only mutate state by modify store.mutate object.

import { create } from "@shined/reactive";

const store = create({
  name: "Pikachu",
});

const changeName = () => {
  store.mutate.name = "Squirtle";
};

Or mutate in components.

import { store } from "./store";

export default function Foo() {
  const state = store.useSnapshot();

  function handleClick() {
    // ❌ Error: Cannot mutate frozen object
    state.name = "Squirtle";
    // ✅ Works
    store.mutate.name = "Squirtle";
  }

  return (
    <>
      <h1>{state.name}</h1>
      <button onClick={handleClick}>mutate name</button>
    </>
  );
}

You can easily restore to initial state.

The newer structuredClone API is used in this function, so consider adding a polyfill if needed.

import { store } from "./store";

export default function Foo() {
  const state = store.useSnapshot();

  return (
    <>
      <h1>{state.name}</h1>
      <button onClick={() => store.restore()}>restore</button>
    </>
  );
}

FAQ

❓ TS type error, readonly type can not be assigned to mutable type

This error commonly occurs when using shineout, antd or other UI component libraries and passing the snapshot to the component props, but the props type can not accept readonly type.

To resolve this type issue, add following line to your global types file, such as global.d.ts or others, and you can head to PR#8 for more details.

// add this typescript `triple-slash directive` to hack type
/// <reference types="@shined/reactive/hack-remove-readonly" />

❓ Accidentally changed the props value in React component passed by parent components, which is frozen snapshot

The React philosophy is that props should be immutable and top-down. So, in principle, you should NOT change the props value inside components.

However, if you do need to do this for reasons such as high historical legacy, migration costs and others like these, you can use the following hook to address it, but it is NOT recommended to use it widely.

import { useEffect, useReducer } from "react";
import { subscribe } from "@shined/reactive";

type PlainObject = Record<PropertyKey, unknown>;

// in TS, if you use JS, you may need to eliminate the type
const useMutableState = <T extends PlainObject>(proxyObj: T) => {
  const [, forceUpdate] = useReducer((c: number) => c + 1, 0);
  useEffect(() => subscribe(proxyObj, forceUpdate), [proxyObj]);
  return proxyObject as T;
};

export function Foo(props) {
  // use `useMutableState` to get mutable state instead of `useSnapshot`
  const state = useMutableState(store.mutate);

  // `AccidentallyChangePropsInsideComponent` will change the props value
  return <AccidentallyChangePropsInsideComponent prop={state} />;
}

Examples

License

Readme

Keywords

none

Package Sidebar

Install

npm i @chrisyu521/reactive

Weekly Downloads

0

Version

0.0.12

License

MIT

Unpacked Size

188 kB

Total Files

48

Last publish

Collaborators

  • chrisyu521