react-containerized-state
Advanced tools
Comparing version 1.0.0 to 1.1.0
import * as React from "react"; | ||
import type { ContainerInitializer, SubscribeCallback, Unsubscribe } from "./types"; | ||
import type { ContainerInitializer, EqualityCheckFunction, SubscribeCallback, Unsubscribe, ValueSelector } from "./types"; | ||
declare class Container<T> { | ||
@@ -8,6 +8,18 @@ private _value; | ||
/** | ||
* Subscribes to the container changes and returns the unsubscribe function. | ||
* Subscribes to the changes of the container's state value | ||
* and returns the unsubscribe function. | ||
*/ | ||
subscribe(subscribeCallback: SubscribeCallback<T>): Unsubscribe; | ||
/** | ||
* Subscribes to the changes of the container's selected state values | ||
* and returns the unsubscribe function. | ||
* | ||
* For more control over emission changes, you may provide a custom equality function. | ||
*/ | ||
selectedSubscribe<P>(selector: ValueSelector<T, P>, subscribeCallback: SubscribeCallback<P>, | ||
/** | ||
* A custom equality function to control emission changes. | ||
*/ | ||
isEqual?: EqualityCheckFunction<P>): Unsubscribe; | ||
/** | ||
* Gets the value of the state. | ||
@@ -26,6 +38,18 @@ * | ||
* | ||
* This is a reactive function and will be updated on state change. | ||
* This is a reactive function and updates on state value change. | ||
*/ | ||
useValue(): T; | ||
/** | ||
* A React hook to read the values of the selected states. | ||
* | ||
* This is a reactive function and updates on selected state values change. | ||
* | ||
* For more control over re-rendering, you may provide a custom equality function. | ||
*/ | ||
useValueSelector<P>(selector: ValueSelector<T, P>, | ||
/** | ||
* A custom equality function to control re-rendering. | ||
*/ | ||
isEqual?: EqualityCheckFunction<P>): P; | ||
/** | ||
* A React hook to update the value of the state and notify the subscribers. | ||
@@ -32,0 +56,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,r,u){void 0===u&&(u=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,u,i)}:function(e,t,r,u){void 0===u&&(u=r),e[u]=t[r]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&__createBinding(t,e,r);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});var React=__importStar(require("react")),use_lazy_initialized_value_1=__importDefault(require("./use-lazy-initialized-value")),use_sync_external_store_1=__importDefault(require("./use-sync-external.store")),Container=function(){function e(e){Object.defineProperty(this,"_value",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_subscribers",{enumerable:!0,configurable:!0,writable:!0,value:void 0});var t="function"==typeof e?e():e;this._value=t,this._subscribers=new Set}return Object.defineProperty(e.prototype,"subscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var t=this;this._subscribers.add(e);return function(){t._subscribers.delete(e)}}}),Object.defineProperty(e.prototype,"getValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){return this._value}}),Object.defineProperty(e.prototype,"updateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(e){this._value=e,this._subscribers.forEach((function(t){return t(e)}))}}),Object.defineProperty(e.prototype,"useValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this,t=(0,use_lazy_initialized_value_1.default)((function(){return e.subscribe.bind(e)})),r=(0,use_lazy_initialized_value_1.default)((function(){return e.getValue.bind(e)})),u=r;return(0,use_sync_external_store_1.default)(t,r,u)}}),Object.defineProperty(e.prototype,"useUpdateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this;return React.useCallback((function(t){var r="function"==typeof t?t(e._value):t;e.updateValue(r)}),[])}}),e}();exports.default=Container; | ||
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var u=Object.getOwnPropertyDescriptor(t,r);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,u)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&__createBinding(t,e,r);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});var React=__importStar(require("react")),use_lazy_initialized_value_1=__importDefault(require("./use-lazy-initialized-value")),use_sync_external_store_1=require("./use-sync-external-store"),Container=function(){function e(e){Object.defineProperty(this,"_value",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_subscribers",{enumerable:!0,configurable:!0,writable:!0,value:void 0});var t="function"==typeof e?e():e;this._value=t,this._subscribers=new Set}return Object.defineProperty(e.prototype,"subscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var t=this;this._subscribers.add(e);return function(){t._subscribers.delete(e)}}}),Object.defineProperty(e.prototype,"selectedSubscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e,t,r){var i=this,u={type:"SELECTED_SUBSCRIBE_ENTRY",selector:e,subscribeCallback:t,isEqual:r};this._subscribers.add(u);return function(){i._subscribers.delete(u)}}}),Object.defineProperty(e.prototype,"getValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){return this._value}}),Object.defineProperty(e.prototype,"updateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var t=this._value;this._value=e,this._subscribers.forEach((function(r){var i;if("type"in r){if("SELECTED_SUBSCRIBE_ENTRY"!==r.type)return;var u=r.selector,n=r.subscribeCallback,a=r.isEqual,l=u(e),o=u(t);if(!!(null!==(i=null==a?void 0:a(o,l))&&void 0!==i?i:Object.is(l,o)))return;n(l)}else{if(Object.is(t,e))return;r(e)}}))}}),Object.defineProperty(e.prototype,"useValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this,t=(0,use_lazy_initialized_value_1.default)((function(){return e.subscribe.bind(e)})),r=(0,use_lazy_initialized_value_1.default)((function(){return e.getValue.bind(e)})),i=r;return(0,use_sync_external_store_1.useSyncExternalStore)(t,r,i)}}),Object.defineProperty(e.prototype,"useValueSelector",{enumerable:!1,configurable:!0,writable:!0,value:function(e,t){var r=this,i=(0,use_lazy_initialized_value_1.default)((function(){return r.subscribe.bind(r)})),u=(0,use_lazy_initialized_value_1.default)((function(){return r.getValue.bind(r)})),n=u;return(0,use_sync_external_store_1.useSyncExternalStoreWithSelector)(i,u,n,e,t)}}),Object.defineProperty(e.prototype,"useUpdateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this;return React.useCallback((function(t){var r="function"==typeof t?t(e._value):t;e.updateValue(r)}),[])}}),e}();exports.default=Container; |
import * as React from "react"; | ||
import type { ContainerInitializer, SubscribeCallback, Unsubscribe } from "./types"; | ||
import type { ContainerInitializer, EqualityCheckFunction, SubscribeCallback, Unsubscribe, ValueSelector } from "./types"; | ||
declare class Container<T> { | ||
@@ -8,6 +8,18 @@ private _value; | ||
/** | ||
* Subscribes to the container changes and returns the unsubscribe function. | ||
* Subscribes to the changes of the container's state value | ||
* and returns the unsubscribe function. | ||
*/ | ||
subscribe(subscribeCallback: SubscribeCallback<T>): Unsubscribe; | ||
/** | ||
* Subscribes to the changes of the container's selected state values | ||
* and returns the unsubscribe function. | ||
* | ||
* For more control over emission changes, you may provide a custom equality function. | ||
*/ | ||
selectedSubscribe<P>(selector: ValueSelector<T, P>, subscribeCallback: SubscribeCallback<P>, | ||
/** | ||
* A custom equality function to control emission changes. | ||
*/ | ||
isEqual?: EqualityCheckFunction<P>): Unsubscribe; | ||
/** | ||
* Gets the value of the state. | ||
@@ -26,6 +38,18 @@ * | ||
* | ||
* This is a reactive function and will be updated on state change. | ||
* This is a reactive function and updates on state value change. | ||
*/ | ||
useValue(): T; | ||
/** | ||
* A React hook to read the values of the selected states. | ||
* | ||
* This is a reactive function and updates on selected state values change. | ||
* | ||
* For more control over re-rendering, you may provide a custom equality function. | ||
*/ | ||
useValueSelector<P>(selector: ValueSelector<T, P>, | ||
/** | ||
* A custom equality function to control re-rendering. | ||
*/ | ||
isEqual?: EqualityCheckFunction<P>): P; | ||
/** | ||
* A React hook to update the value of the state and notify the subscribers. | ||
@@ -32,0 +56,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
import*as e from"react";import r from"./use-lazy-initialized-value";import t from"./use-sync-external.store";var u=function(){function u(e){Object.defineProperty(this,"_value",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_subscribers",{enumerable:!0,configurable:!0,writable:!0,value:void 0});var r="function"==typeof e?e():e;this._value=r,this._subscribers=new Set}return Object.defineProperty(u.prototype,"subscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var r=this;this._subscribers.add(e);return function(){r._subscribers.delete(e)}}}),Object.defineProperty(u.prototype,"getValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){return this._value}}),Object.defineProperty(u.prototype,"updateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(e){this._value=e,this._subscribers.forEach((function(r){return r(e)}))}}),Object.defineProperty(u.prototype,"useValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this,u=r((function(){return e.subscribe.bind(e)})),i=r((function(){return e.getValue.bind(e)}));return t(u,i,i)}}),Object.defineProperty(u.prototype,"useUpdateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var r=this;return e.useCallback((function(e){var t="function"==typeof e?e(r._value):e;r.updateValue(t)}),[])}}),u}();export default u; | ||
import*as e from"react";import r from"./use-lazy-initialized-value";import{useSyncExternalStore as t,useSyncExternalStoreWithSelector as u}from"./use-sync-external-store";var i=function(){function i(e){Object.defineProperty(this,"_value",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_subscribers",{enumerable:!0,configurable:!0,writable:!0,value:void 0});var r="function"==typeof e?e():e;this._value=r,this._subscribers=new Set}return Object.defineProperty(i.prototype,"subscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var r=this;this._subscribers.add(e);return function(){r._subscribers.delete(e)}}}),Object.defineProperty(i.prototype,"selectedSubscribe",{enumerable:!1,configurable:!0,writable:!0,value:function(e,r,t){var u=this,i={type:"SELECTED_SUBSCRIBE_ENTRY",selector:e,subscribeCallback:r,isEqual:t};this._subscribers.add(i);return function(){u._subscribers.delete(i)}}}),Object.defineProperty(i.prototype,"getValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){return this._value}}),Object.defineProperty(i.prototype,"updateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(e){var r=this._value;this._value=e,this._subscribers.forEach((function(t){var u;if("type"in t){if("SELECTED_SUBSCRIBE_ENTRY"!==t.type)return;var i=t.selector,n=t.subscribeCallback,a=t.isEqual,l=i(e),b=i(r);if(!!(null!==(u=null==a?void 0:a(b,l))&&void 0!==u?u:Object.is(l,b)))return;n(l)}else{if(Object.is(r,e))return;t(e)}}))}}),Object.defineProperty(i.prototype,"useValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var e=this,u=r((function(){return e.subscribe.bind(e)})),i=r((function(){return e.getValue.bind(e)}));return t(u,i,i)}}),Object.defineProperty(i.prototype,"useValueSelector",{enumerable:!1,configurable:!0,writable:!0,value:function(e,t){var i=this,n=r((function(){return i.subscribe.bind(i)})),a=r((function(){return i.getValue.bind(i)}));return u(n,a,a,e,t)}}),Object.defineProperty(i.prototype,"useUpdateValue",{enumerable:!1,configurable:!0,writable:!0,value:function(){var r=this;return e.useCallback((function(e){var t="function"==typeof e?e(r._value):e;r.updateValue(t)}),[])}}),i}();export default i; |
export { default as createStateContainer } from "./create-state-container"; | ||
export type { ContainerInitializer, EqualityCheckFunction, SubscribeCallback, Unsubscribe, ValueSelector, } from "./types"; |
export type CallableFunction<TArgs extends any[] = [], TReturn = void> = (...args: TArgs) => TReturn; | ||
export type SubscribeCallback<T> = (value: T) => void; | ||
export type Unsubscribe = () => void; | ||
export type ValueSelector<T, P> = (value: T) => P; | ||
export type EqualityCheckFunction<P> = (a: P, b: P) => boolean; | ||
export type SelectedSubscribeEntry<T, P> = { | ||
type: "SELECTED_SUBSCRIBE_ENTRY"; | ||
subscribeCallback: SubscribeCallback<P>; | ||
selector: ValueSelector<T, P>; | ||
isEqual?: EqualityCheckFunction<P>; | ||
}; | ||
export type ContainerInitializer<T> = T | CallableFunction<[], T>; |
export { default as createStateContainer } from "./create-state-container"; | ||
export type { ContainerInitializer, EqualityCheckFunction, SubscribeCallback, Unsubscribe, ValueSelector, } from "./types"; |
export type CallableFunction<TArgs extends any[] = [], TReturn = void> = (...args: TArgs) => TReturn; | ||
export type SubscribeCallback<T> = (value: T) => void; | ||
export type Unsubscribe = () => void; | ||
export type ValueSelector<T, P> = (value: T) => P; | ||
export type EqualityCheckFunction<P> = (a: P, b: P) => boolean; | ||
export type SelectedSubscribeEntry<T, P> = { | ||
type: "SELECTED_SUBSCRIBE_ENTRY"; | ||
subscribeCallback: SubscribeCallback<P>; | ||
selector: ValueSelector<T, P>; | ||
isEqual?: EqualityCheckFunction<P>; | ||
}; | ||
export type ContainerInitializer<T> = T | CallableFunction<[], T>; |
{ | ||
"name": "react-containerized-state", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Fast and minimal state container which can be used and shared across React or non-React components.", | ||
@@ -43,2 +43,5 @@ "main": "./dist/index.js", | ||
}, | ||
"dependencies": { | ||
"use-sync-external-store": "^1.2.2" | ||
}, | ||
"devDependencies": { | ||
@@ -49,2 +52,3 @@ "@types/node": "^20.12.12", | ||
"@types/semver": "^7.5.8", | ||
"@types/use-sync-external-store": "^0.0.6", | ||
"@typescript-eslint/eslint-plugin": "^7.9.0", | ||
@@ -51,0 +55,0 @@ "@typescript-eslint/parser": "^7.9.0", |
140
README.md
@@ -17,3 +17,3 @@ # react-containerized-state | ||
## Usage | ||
## Basic usage | ||
@@ -86,2 +86,107 @@ Consider the following example: | ||
## Usage with selectors | ||
There may be situations where you have to store a complex state in your container (**It is recommended to have different small containers instead of several large ones**). In these cases, you may not want to subscribe to all the fields of the complex state. Instead you want to subscribe to different parts in different components or modules. | ||
Consider the following example: | ||
```tsx | ||
import { createStateContainer } from "react-containerized-state"; | ||
// You can move this container to a separate module and share it across your app | ||
const complexState = createStateContainer({ a: 1, b: "2" }); | ||
const Controls = () => { | ||
const updateState = complexState.useUpdateValue(); | ||
return ( | ||
<div> | ||
<button | ||
onClick={() => { | ||
updateState(s => ({ | ||
...s, | ||
a: s.a + 1, | ||
})); | ||
}} | ||
> | ||
Update State.A | ||
</button> | ||
<button | ||
onClick={() => { | ||
updateState(s => ({ | ||
...s, | ||
b: String(Number(s.b) + 1), | ||
})); | ||
}} | ||
> | ||
Update State.B | ||
</button> | ||
</div> | ||
); | ||
}; | ||
const DisplayA = () => { | ||
const a = complexState.useValueSelector(value => value.a); | ||
return <div>State.A: {a}</div>; | ||
}; | ||
const DisplayB = () => { | ||
const b = complexState.useValueSelector(value => value.b); | ||
return <div>State.B: {b}</div>; | ||
}; | ||
const Container = () => { | ||
return ( | ||
<div> | ||
<h2>Container</h2> | ||
<Controls /> | ||
<DisplayA /> | ||
<DisplayB /> | ||
</div> | ||
); | ||
}; | ||
const Page = () => { | ||
return ( | ||
<main> | ||
<Container /> | ||
</main> | ||
); | ||
}; | ||
export default Page; | ||
``` | ||
In this example, when the user clicks on the buttons of the `Controls` component, the state changes and each component that is subscribed to a part of the container's state via `useValueSelector` hook will be notified and re-rendered as a result. In other words, `DisplayA` component re-renders only when `value.a` changes (same thing for the `DisplayB` component and the value of `value.b` state.). | ||
You can pass in any selector you want. Think of selectors as a state transformer where you can transform a complex state into another simpler shape. | ||
For example: | ||
```tsx | ||
const { a, b } = complexState.useValueSelector(value => ({ a: value.a, b: value.b})); | ||
const valueOfB = complexState.useValueSelector(value => value.b); | ||
``` | ||
Cool, huh? | ||
So, what about using the complex state in a non-React environment? | ||
You can opt-in `selectedSubscribe` instead of `subscribe`. | ||
For example: | ||
```ts | ||
// Logs the new value of `value.a` on selected state changes | ||
complexState.selectedSubscribe(value => value.a, console.log); | ||
``` | ||
## More control of re-rendering and emission changes? | ||
For more control over re-rendering (in React environment) and emission changes (in non-React environment) try to pass in `isEqual` parameter to the `useValueSelector` and `selectedSubscribe` (check the API section for more information). | ||
> By default, we are using `Object.is` as equality check function. | ||
## API | ||
@@ -94,6 +199,21 @@ | ||
/** | ||
* Subscribes to the container changes and returns the unsubscribe function. | ||
* Subscribes to the changes of the container's state value | ||
* and returns the unsubscribe function. | ||
*/ | ||
subscribe(subscribeCallback: (value: T) => void): () => void; | ||
/** | ||
* Subscribes to the changes of the container's selected state values | ||
* and returns the unsubscribe function. | ||
* | ||
* For more control over emission changes, you may provide a custom equality function. | ||
*/ | ||
selectedSubscribe<P>( | ||
selector: (value: T) => P, | ||
subscribeCallback: (value: P) => void, | ||
/** | ||
* A custom equality function to control emission changes. | ||
*/ | ||
isEqual?: (a: P, b: P) => boolean, | ||
): () => void; | ||
/** | ||
* Gets the value of the state. | ||
@@ -112,6 +232,20 @@ * | ||
* | ||
* This is a reactive function and will be updated on state change. | ||
* This is a reactive function and updates on state value change. | ||
*/ | ||
useValue(): T; | ||
/** | ||
* A React hook to read the values of the selected states. | ||
* | ||
* This is a reactive function and updates on selected state values change. | ||
* | ||
* For more control over re-rendering, you may provide a custom equality function. | ||
*/ | ||
useValueSelector( | ||
selector: (value: T) => P, | ||
/** | ||
* A custom equality function to control re-rendering. | ||
*/ | ||
isEqual?: (a: P, b: P) => boolean, | ||
): P; | ||
/** | ||
* A React hook to update the value of the state and notify the subscribers. | ||
@@ -118,0 +252,0 @@ */ |
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
26344
167
263
3
24
+ Addeduse-sync-external-store@1.4.0(transitive)