react-usectx 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,32 @@ const setMyContext = updateCtx("stateName");
45
45
  setMyContext({ name: "Rick Ross" });
46
46
  ```
47
47
 
48
+ ### useReducer
49
+
50
+ ```js
51
+ import { useReducer } from "react-usectx";
52
+
53
+ const fullName = useReducer("stateName", (data) => {
54
+ return `${data.firstName} ${data.lastName}`;
55
+ });
56
+ ```
57
+
58
+ ## Params
59
+
60
+ ### stateName
61
+
62
+ ```js
63
+ const myContextID = "MyUniqueIdentifier";
64
+
65
+ useCtx(myContextID);
66
+ ```
67
+
68
+ The stateName serves as a unique identifier for a shared state object. Internally, the state manager maintains a list of state entries, each associated with a distinct stateName.
69
+
70
+ When any of the provided functions—useCtx, updateCtx, or getCtx—are invoked, the state manager checks whether a state object with the specified stateName already exists. If it does not, a new state entry is automatically created and initialized.
71
+
72
+ This mechanism enables global state sharing across components. For example, calling getCtx("example-1") in one component will return the same state reference when called with "example-1" in any other component within the same document. This allows for seamless state synchronization across different parts of your application without the need for explicit context providers.
73
+
48
74
  ## Example
49
75
 
50
76
  ```js
@@ -77,3 +103,38 @@ function Title() {
77
103
  );
78
104
  }
79
105
  ```
106
+
107
+ ## Example with reducer
108
+
109
+ ```js
110
+ function Page() {
111
+ const [title, setTitle] = useCtx("title");
112
+
113
+ return (
114
+ <div className="wrapper">
115
+ <form>
116
+ <label htmlFor="text">Enter text</label>
117
+ <input
118
+ type="text"
119
+ id="text"
120
+ onChange={(e) => setTitle(e.target.value)}
121
+ />
122
+ </form>
123
+ <Title></Title>
124
+ <p>{title}</p>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ function Title() {
130
+ const reducedTitle: string = useReducer(
131
+ "title",
132
+ (title) => `Title is: ${title.toUpperCase()}`
133
+ );
134
+ return (
135
+ <h1>
136
+ <strong>{reducedTitle}</strong>
137
+ </h1>
138
+ );
139
+ }
140
+ ```
package/context.js ADDED
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { useSyncExternalStore } from "react";
3
+ import { CreateContext } from "./create-context";
4
+ const cache = {};
5
+ const getInstance = (name) => {
6
+ if (!cache[name]) {
7
+ cache[name] = new CreateContext();
8
+ }
9
+ return cache[name];
10
+ };
11
+ export function updateCtx(name) {
12
+ const ctx = getInstance(name);
13
+ return ctx.commit.bind(ctx);
14
+ }
15
+ export function getCtx(name) {
16
+ const context = getInstance(name);
17
+ const subscription = (callback) => {
18
+ context.subscribe(callback);
19
+ return () => context.unsubscribe(callback);
20
+ };
21
+ return useSyncExternalStore(subscription, context.getState.bind(context), context.getState.bind(context));
22
+ }
23
+ export function useCtx(name) {
24
+ return [getCtx(name), updateCtx(name)];
25
+ }
26
+ const reducerCache = {};
27
+ export function useReducer(name, modiFier, useCache = true) {
28
+ if (!reducerCache[name]) {
29
+ reducerCache[name] = new WeakMap();
30
+ }
31
+ const context = getInstance(name);
32
+ const _getState = context.getState.bind(context);
33
+ const getState = () => {
34
+ const state = _getState();
35
+ const cache = reducerCache[name].get(state);
36
+ if (cache && useCache) {
37
+ return cache;
38
+ }
39
+ const res = modiFier(state);
40
+ reducerCache[name].set(state, res);
41
+ return res;
42
+ };
43
+ const subscription = (callback) => {
44
+ context.subscribe(callback);
45
+ return () => context.unsubscribe(callback);
46
+ };
47
+ return useSyncExternalStore(subscription, getState, getState);
48
+ }
@@ -0,0 +1,23 @@
1
+ export class CreateContext {
2
+ #eventHandlers = [];
3
+ #state = null;
4
+ subscribe(handler) {
5
+ this.#eventHandlers.push(handler);
6
+ }
7
+ unsubscribe(handler) {
8
+ const index = this.#eventHandlers.indexOf(handler);
9
+ if (index !== -1) {
10
+ this.#eventHandlers.splice(index, 1);
11
+ }
12
+ }
13
+ commit(state) {
14
+ this.setState(state);
15
+ this.#eventHandlers.forEach((callback) => callback.call(undefined, state));
16
+ }
17
+ setState(state) {
18
+ this.#state = state;
19
+ }
20
+ getState() {
21
+ return this.#state;
22
+ }
23
+ }
package/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { useCtx as default } from "./src/context";
2
- export { getCtx } from "./src/context";
3
- export { updateCtx } from "./src/context";
1
+ export { useCtx as default } from "./context";
2
+ export { getCtx, useReducer, updateCtx } from "./context";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-usectx",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "repository": {
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "test": "jest",
20
- "build": "npx tsc index.ts --target es2017"
20
+ "build": "npx tsc ./src/index.ts --target es2022 --outDir ./"
21
21
  },
22
22
  "author": "",
23
23
  "homepage": "https://jsf7.dev/react-usectx",
package/src/context.ts CHANGED
@@ -1,37 +1,66 @@
1
- 'use client'
1
+ "use client";
2
2
 
3
- import { ReactNode, useSyncExternalStore } from 'react'
4
- import { CreateContext } from './create-context'
3
+ import { ReactNode, useSyncExternalStore } from "react";
4
+ import { CreateContext } from "./create-context";
5
5
 
6
- type cache = { [key: string]: InstanceType<typeof CreateContext> }
6
+ type cache = { [key: string]: InstanceType<typeof CreateContext> };
7
7
 
8
- const cache: cache = {}
8
+ const cache: cache = {};
9
9
 
10
10
  const getInstance = (name: string): InstanceType<typeof CreateContext> => {
11
- if (!cache[name]) {
12
- cache[name] = new CreateContext()
13
- }
14
- return cache[name]
15
- }
11
+ if (!cache[name]) {
12
+ cache[name] = new CreateContext();
13
+ }
14
+ return cache[name];
15
+ };
16
16
 
17
17
  export function updateCtx(name: string): Function {
18
- const ctx = getInstance(name)
19
- return ctx.commit.bind(ctx)
18
+ const ctx = getInstance(name);
19
+ return ctx.commit.bind(ctx);
20
20
  }
21
21
 
22
22
  export function getCtx<Snapshot>(name: string): Snapshot {
23
- const context = getInstance(name)
24
- const subscription = (callback: Function) => {
25
- context.subscribe(callback)
26
- return () => context.unsubscribe(callback)
27
- }
28
- return useSyncExternalStore(
29
- subscription,
30
- context.getState.bind(context),
31
- context.getState.bind(context)
32
- )
23
+ const context = getInstance(name);
24
+ const subscription = (callback: Function) => {
25
+ context.subscribe(callback);
26
+ return () => context.unsubscribe(callback);
27
+ };
28
+ return useSyncExternalStore(
29
+ subscription,
30
+ context.getState.bind(context),
31
+ context.getState.bind(context)
32
+ );
33
33
  }
34
34
 
35
35
  export function useCtx(name: string): [ReactNode, Function] {
36
- return [getCtx(name), updateCtx(name)]
36
+ return [getCtx(name), updateCtx(name)];
37
+ }
38
+
39
+ const reducerCache: { [key: string]: WeakMap<any, any> } = {};
40
+
41
+ export function useReducer<Snapshot>(
42
+ name: string,
43
+ modiFier: (data: any) => Snapshot,
44
+ useCache: boolean = true
45
+ ): Snapshot {
46
+ if (!reducerCache[name]) {
47
+ reducerCache[name] = new WeakMap();
48
+ }
49
+ const context = getInstance(name);
50
+ const _getState = context.getState.bind(context);
51
+ const getState = () => {
52
+ const state = _getState();
53
+ const cache = reducerCache[name].get(state);
54
+ if (cache && useCache) {
55
+ return cache;
56
+ }
57
+ const res = modiFier(state);
58
+ reducerCache[name].set(state, res);
59
+ return res;
60
+ };
61
+ const subscription = (callback: Function) => {
62
+ context.subscribe(callback);
63
+ return () => context.unsubscribe(callback);
64
+ };
65
+ return useSyncExternalStore(subscription, getState, getState);
37
66
  }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { useCtx as default } from "./context";
2
+ export { getCtx, useReducer, updateCtx } from "./context";
package/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { useCtx as default } from "./src/context";
2
- export { getCtx } from "./src/context";
3
- export { updateCtx } from "./src/context";
package/intex.ts DELETED
@@ -1,3 +0,0 @@
1
- export { useCtx as default } from "./src/context";
2
- export { getCtx } from "./src/context";
3
- export { updateCtx } from "./src/context";
package/src/context.js DELETED
@@ -1,25 +0,0 @@
1
- 'use client';
2
- import { useSyncExternalStore } from 'react';
3
- import { CreateContext } from './create-context';
4
- const cache = {};
5
- const getInstance = (name) => {
6
- if (!cache[name]) {
7
- cache[name] = new CreateContext();
8
- }
9
- return cache[name];
10
- };
11
- export function updateCtx(name) {
12
- const ctx = getInstance(name);
13
- return ctx.commit.bind(ctx);
14
- }
15
- export function getCtx(name) {
16
- const context = getInstance(name);
17
- const subscription = (callback) => {
18
- context.subscribe(callback);
19
- return () => context.unsubscribe(callback);
20
- };
21
- return useSyncExternalStore(subscription, context.getState.bind(context), context.getState.bind(context));
22
- }
23
- export function useCtx(name) {
24
- return [getCtx(name), updateCtx(name)];
25
- }
@@ -1,38 +0,0 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
- if (kind === "m") throw new TypeError("Private method is not writable");
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
- };
12
- var _CreateContext_eventHandlers, _CreateContext_state;
13
- export class CreateContext {
14
- constructor() {
15
- _CreateContext_eventHandlers.set(this, []);
16
- _CreateContext_state.set(this, null);
17
- }
18
- subscribe(handler) {
19
- __classPrivateFieldGet(this, _CreateContext_eventHandlers, "f").push(handler);
20
- }
21
- unsubscribe(handler) {
22
- const index = __classPrivateFieldGet(this, _CreateContext_eventHandlers, "f").indexOf(handler);
23
- if (index !== -1) {
24
- __classPrivateFieldGet(this, _CreateContext_eventHandlers, "f").splice(index, 1);
25
- }
26
- }
27
- commit(state) {
28
- this.setState(state);
29
- __classPrivateFieldGet(this, _CreateContext_eventHandlers, "f").forEach((callback) => callback.call(undefined, state));
30
- }
31
- setState(state) {
32
- __classPrivateFieldSet(this, _CreateContext_state, state, "f");
33
- }
34
- getState() {
35
- return __classPrivateFieldGet(this, _CreateContext_state, "f");
36
- }
37
- }
38
- _CreateContext_eventHandlers = new WeakMap(), _CreateContext_state = new WeakMap();