react-hooks-global-states 7.0.10 β†’ 7.0.12

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.
Files changed (2) hide show
  1. package/README.md +167 -381
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -2,65 +2,57 @@
2
2
 
3
3
  ![Image John Avatar](https://raw.githubusercontent.com/johnny-quesada-developer/global-hooks-example/main/public/avatar2.jpeg)
4
4
 
5
- Hi There! Welcome to **react-hooks-global-states** your New State Management Solution for React Components πŸš€
5
+ Effortless **global state management** for `React` & `React Native` & `Preact`! πŸš€ Define a **global state in just one line of code** and enjoy **lightweight, flexible, and scalable** state management. Try it now on **[CodePen](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010)** and see it in action! ✨
6
6
 
7
- Are you looking for a solution to manage **global state** in your **React components**? Look no further!
7
+ ---
8
8
 
9
- **react-hooks-global-states** is your option for efficiently handling global state management in your React applications.
9
+ ## πŸ”— Explore More
10
10
 
11
- One line of code for a **global state**! try it out now on [CODEPEN-react-global-state-hooks](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010) and witness the magic ✨.
11
+ - **[Live Example](https://johnny-quesada-developer.github.io/react-global-state-hooks-example/)** πŸ“˜
12
+ - **[React Native Integration](https://www.npmjs.com/package/react-native-global-state-hooks/)** πŸ“±
13
+ - **[Todo-List Example](https://github.com/johnny-quesada-developer/todo-list-with-global-hooks.git/)** πŸ“
14
+ - **[Video Overview](https://www.youtube.com/watch?v=1UBqXk2MH8I/)** πŸŽ₯
15
+ - **[GitHub Repository](https://github.com/johnny-quesada-developer/global-hooks-example/)** 🧩
12
16
 
13
- For a deeper dive into how these hooks work, check out a comprehensive example at [react-global-state-hooks-example](https://johnny-quesada-developer.github.io/react-global-state-hooks-example/) πŸ“˜.
17
+ Works seamlessly with **React & React Native**:
14
18
 
15
- Want to explore how it works with **React Native**? Head over to [react-native-global-state-hooks](https://www.npmjs.com/package/react-native-global-state-hooks) for a hands-on experience πŸ“±. You can also explore a **TODO-LIST** example using global state and asynchronous storage by heading to [todo-list-with-global-hooks](https://github.com/johnny-quesada-developer/todo-list-with-global-hooks.git) πŸ“.
19
+ - **[react-global-state-hooks](https://www.npmjs.com/package/react-global-state-hooks)** for web applications.
20
+ - **[react-native-global-state-hooks](https://www.npmjs.com/package/react-native-global-state-hooks)** for React Native projects.
16
21
 
17
- For a more visual introduction, watch our informative video [here!](https://www.youtube.com/watch?v=1UBqXk2MH8I) πŸŽ₯ and dive into the code on [global-hooks-example](https://github.com/johnny-quesada-developer/global-hooks-example) 🧩.
22
+ ---
18
23
 
19
- The best part? **react-hooks-global-states** is compatible with both **React** and **React Native**. If you're building web applications, explore [**react-global-state-hooks**](https://www.npmjs.com/package/react-global-state-hooks), and for your React Native projects, check out [**react-native-global-state-hooks**](https://www.npmjs.com/package/react-native-global-state-hooks). These specialized libraries extend the capabilities of **react-hooks-global-states** to perfectly fit your specific development environments. Discover the ease of global state management today! 🌐
24
+ ## πŸ›  Creating a Global State
20
25
 
21
- # Creating a global state
26
+ Define a **global state** in **one line**:
22
27
 
23
- We are gonna create a global state hook **useCount** with one line of code.
24
-
25
- ```ts
28
+ ```tsx
26
29
  import { createGlobalState } from 'react-hooks-global-states/createGlobalState';
27
-
28
30
  export const useCount = createGlobalState(0);
29
31
  ```
30
32
 
31
- That's it! Welcome to global hooks. Now, you can use this state wherever you need it in your application.
33
+ Now, use it inside a component:
32
34
 
33
- Let's see how to use it inside a simple **component**
34
-
35
- ```ts
35
+ ```tsx
36
36
  const [count, setCount] = useCount();
37
-
38
37
  return <Button onClick={() => setCount((count) => count + 1)}>{count}</Button>;
39
38
  ```
40
39
 
41
- Isn't it cool? It works just like a regular **useState**. Notice the only difference is that now you don't need to provide the initial value since this is a global hook, and the initial value has already been provided.
40
+ Works just like **useState**, but the **state is shared globally**! πŸŽ‰
42
41
 
43
- # Selectors
42
+ ---
44
43
 
45
- What if you already have a global state that you want to subscribe to, but you don't want your component to listen to all the changes of the state, only a small portion of it? Let's create a more complex **state**
44
+ ## 🎯 Selectors: Subscribing to Specific State Changes
46
45
 
47
- ```ts
48
- import { createGlobalState } from 'react-hooks-global-states/createGlobalState';
46
+ For **complex state objects**, you can subscribe to specific properties instead of the entire state:
49
47
 
50
- export const useContacts = createGlobalState({
51
- isLoading: true,
52
- entities: Contact[],
53
- selected: Set<number>,
54
- });
48
+ ```tsx
49
+ export const useContacts = createGlobalState({ entities: [], selected: new Set<number>() });
55
50
  ```
56
51
 
57
- Now, let's say we have a situation where we want to access only the list of contacts. We don't care about the rest of the state.
52
+ To access only the `entities` property:
58
53
 
59
54
  ```tsx
60
- // That's it. With that simple selector, we now get the list of contacts,
61
- // and the component will only re-render if the property **entities** changes on the global state
62
- const [contacts] = useContacts((state) => state.entities]);
63
-
55
+ const [contacts] = useContacts((state) => state.entities);
64
56
  return (
65
57
  <ul>
66
58
  {contacts.map((contact) => (
@@ -70,297 +62,152 @@ return (
70
62
  );
71
63
  ```
72
64
 
73
- What about special cases, like when you have a map instead of an array and want to extract a list of contacts? It's common to use selectors that return a new array, but this can cause React to re-render because the new array has a different reference than the previous one.
74
-
75
- ```tsx
76
- export const useContacts = createGlobalState({
77
- isLoading: true,
78
- entities: Map<number, Contact>,
79
- selected: Set<number>,
80
- });
81
-
82
- // The selector is simply a standard selector used to extract the values from the map.
83
-
84
- const [contacts] = useContacts((state) => [...state.entities.values()], {
85
- // The isEqualRoots function allows you to create your own validation logic for determining when to recompute the selector.
86
- isEqualRoot: (a, b) => a.entities === b.entities,
87
- });
88
- ```
89
-
90
- Okay, everything works when the changes come from the state, but what happens if I want to recompute the selector based on the internal state of the component?
65
+ ### πŸ“Œ Using Dependencies in Selectors
91
66
 
92
- **component.ts**
67
+ You can also add **dependencies** to a selector. This is useful when you want to derive state based on another piece of state (e.g., a filtered list). For example, if you're filtering contacts based on a `filter` value:
93
68
 
94
69
  ```tsx
95
- const [filter, setFilter] = useState('');
96
-
97
70
  const [contacts] = useContacts(
98
- (state) => [...state.entities.values()].filter((item) => item.name.includes(filter)),
99
- {
100
- isEqualRoot: (a, b) => a.entities === b.entities,
101
- /**
102
- * Easy to understand, right? With the dependencies prop, you can,
103
- * just like with any other hook, provide a collection of values that will be compared during each render cycle
104
- * to determine if the selector should be recomputed.*/
105
- dependencies: [filter],
106
- }
71
+ (state) => state.entities.filter((item) => item.name.includes(filter)),
72
+ [filter]
107
73
  );
108
74
  ```
109
75
 
110
- And finally, what if you need to reuse this selector throughout your application and don't want to duplicate code?
76
+ Alternatively, you can pass dependencies inside an **options object**:
111
77
 
112
78
  ```tsx
113
- export const useContacts = createGlobalState({
114
- isLoading: true,
115
- entities: Map<number, Contact>,
116
- selected: Set<number>,
117
- });
118
-
119
- const useContactsArray = useContacts.createSelectorHook((state) => [...state.entities.values()], {
79
+ const [contacts] = useContacts((state) => state.entities.filter((item) => item.name.includes(filter)), {
80
+ dependencies: [filter],
120
81
  isEqualRoot: (a, b) => a.entities === b.entities,
121
82
  });
122
83
  ```
123
84
 
124
- Now inside your component just call the new hook
85
+ Unlike Redux, where only **root state changes trigger re-selection**, this approach ensures that **derived values recompute when dependencies change** while maintaining performance.
125
86
 
126
- **component.ts**
87
+ ---
127
88
 
128
- ```tsx
129
- const [filter, setFilter] = useState('');
89
+ ## πŸ”„ Reusing Selectors
130
90
 
131
- const [contacts] = useContactsArray((entities) => entities.name.includes(filter), {
132
- dependencies: [filter],
133
- });
134
- ```
135
-
136
- Or you can create another selectorHook from your **useContactsArray**
91
+ ### πŸ“Œ Creating a Selector
137
92
 
138
- ```ts
139
- const useContactsArray = useContacts.createSelectorHook((state) => [...state.entities.values()], {
140
- isEqualRoot: (a, b) => a.entities === b.entities,
141
- });
142
-
143
- const useContactsLength = useContactsArray.createSelectorHook((entities) => entities.length);
93
+ ```tsx
94
+ export const useContactsArray = useContacts.createSelectorHook((state) => state.entities);
95
+ export const useContactsCount = useContactsArray.createSelectorHook((entities) => entities.length);
144
96
  ```
145
97
 
146
- Or you can create a custom hook
98
+ ### πŸ“Œ Using Selectors in Components
147
99
 
148
100
  ```tsx
149
- const useFilteredContacts = (filter: string) => {
150
- const [contacts] = useContactsArray((entities) => entities.name.includes(filter), {
151
- dependencies: [filter],
152
- });
153
-
154
- return contacts;
155
- };
101
+ const [contacts] = useContactsArray();
102
+ const [count] = useContactsCount();
156
103
  ```
157
104
 
158
- To summarize
105
+ #### βœ… Selectors support inline selectors and dependencies
159
106
 
160
- ```tsx
161
- const [filter, setFilter] = useState('');
162
-
163
- const [contacts] = useContacts((state) => state.contacts.filter((contact) => contact.name.includes(filter)), {
164
- /**
165
- * You can use the `isEqualRoot` to validate if the values before the selector are equal.
166
- * This validation will run before `isEqual` and if the result is true the selector will not be recomputed.
167
- * If the result is true the re-render of the component will be prevented.
168
- */
169
- isEqualRoot: (r1, r2) => r1.filter === r2.filter,
170
-
171
- /**
172
- * You can use the `isEqual` to validate if the values after the selector are equal.
173
- * This validation will run after the selector computed a new value...
174
- * and if the result is true it will prevent the re-render of the component.
175
- */
176
- isEqual: (filter1, filter2) => filter1 === filter2,
177
-
178
- /**
179
- * You can use the `dependencies` array as with regular hooks to to force the recomputation of the selector.
180
- * Is important ot mention that changes in the dependencies will not trigger a re-render of the component...
181
- * Instead the recomputation of the selector will returned immediately.
182
- */
183
- dependencies: [filter],
184
- });
107
+ You can still **use dependencies** inside a selector hook:
185
108
 
186
- return (
187
- <ul>
188
- {contacts.map((contact) => (
189
- <li key={contact.id}>{contact.name}</li>
190
- ))}
191
- </ul>
109
+ ```tsx
110
+ const [filteredContacts] = useContactsArray(
111
+ (contacts) => contacts.filter((c) => c.name.includes(filter)),
112
+ [filter]
192
113
  );
193
114
  ```
194
115
 
195
- Btw, If you want to perform a shallow comparison between the previous and new values, you can use the **shallowCompare** function from the library.
196
-
197
- ```TSX
198
- ({
199
- /**
200
- * You can use the `shallowCompare` from the GlobalStore.utils to compare the values at first level.
201
- */
202
- isEqual: shallowCompare,
203
- })
204
- ```
205
-
206
- Just remember, you can select or derive different values from the global state endlessly, but the state mutator will remain the same throughout the hooks.
207
-
208
- More examples:
209
-
210
- ```ts
211
- const useFilter = useContacts.createSelectorHook(({ filter }) => filter);
116
+ #### βœ… Selector hooks share the same state mutator
212
117
 
213
- const useContactsArray = useContacts.createSelectorHook(({ items }) => items);
118
+ The **stateMutator remains the same** across all derived selectors, meaning actions and setState functions stay consistent.
214
119
 
215
- const useContactsLength = useContactsArray.createSelectorHook((items) => items.length);
120
+ ```tsx
121
+ const [actions1] = useContactsArray();
122
+ const [actions2] = useContactsCount();
216
123
 
217
- const useIsContactsEmpty = useContactsLength.createSelectorHook((length) => !length);
124
+ console.log(actions1 === actions2); // true
218
125
  ```
219
126
 
220
- It can't get any simpler, right? Everything is connected, everything is reactive. Plus, these hooks are strongly typed, so if you're working with **TypeScript**, you'll absolutely love it.
221
-
222
- Each selector hook is reactive only to the fragment/derived of the state returned by the selector. And again you can optimize it by using the **isEqualRoot** and **isEqual** functions, which help avoid recomputing the selector if the root state or the fragment hasn't changed.
223
-
224
- # State actions
225
-
226
- Is common and often necessary to restrict the manipulation of state to a specific set of actions or operations. To achieve this, we can simplify the process by adding a custom API to the configuration of our **useContacts**.
127
+ ---
227
128
 
228
- By defining a custom API for the **useContacts**, we can encapsulate and expose only the necessary actions or operations that are allowed to modify the state. This provides a controlled interface for interacting with the state, ensuring that modifications stick to the desired restrictions.
129
+ ## πŸŽ› State Actions: Controlling State Modifications
229
130
 
230
- ```ts
231
- import { createGlobalState } from 'react-hooks-global-states/createGlobalState';
131
+ Restrict **state modifications** by defining custom actions:
232
132
 
133
+ ```tsx
233
134
  export const useContacts = createGlobalState(
135
+ { filter: '', items: [] },
234
136
  {
235
- isLoading: true,
236
- filter: '',
237
- items: [] as Contact[],
238
- },
239
- {
240
- callbacks: {
241
- onInit: ({ setState }) => {
242
- // fetch contacts
137
+ actions: {
138
+ async fetch() {
139
+ return async ({ setState }) => {
140
+ const items = await fetchItems();
141
+ setState({ items });
142
+ };
243
143
  },
244
- actions: {
245
- setFilter(filter: string) {
246
- return ({ setState }) => {
247
- setState((state) => ({
248
- ...state,
249
- filter,
250
- }));
251
- };
252
- },
144
+ setFilter(filter: string) {
145
+ return ({ setState }) => {
146
+ setState((state) => ({ ...state, filter }));
147
+ };
253
148
  },
254
149
  },
255
150
  }
256
151
  );
257
152
  ```
258
153
 
259
- That's it! In this updated version, the **useContacts** hook will no longer return [**state**, **stateMutator:Setter<State>**] but instead will return [**state**, **stateMutator:ActionCollectionResult<State>**]. This change will provide a more intuitive and convenient way to access and interact with the state and its associated actions.
260
-
261
- Let's see how that will look now into our **FilterBar.tsx**
154
+ Now, instead of `setState`, the hook returns **actions**:
262
155
 
263
156
  ```tsx
264
- const [filter, { setFilter }] = useFilter();
265
-
266
- return <TextInput onChangeText={setFilter} />;
157
+ const [filter, { setFilter }] = useContacts();
267
158
  ```
268
159
 
269
- Yeah, that's it! All the **derived states** and **emitters** (we will talk about **emitters** this later) will inherit the new actions interface.
160
+ ---
270
161
 
271
- # State Controls
162
+ ## 🌍 Accessing Global State Outside Components
272
163
 
273
- If you need to access the global state outside of a component or a hook without subscribing to state changes, or even inside a **ClassComponent**, you can use:
164
+ Use `stateControls()` to **retrieve or update state outside React components**:
274
165
 
275
166
  ```tsx
276
- useContacts.stateControls: () => [stateRetriever, stateMutator, metadataRetriever];
277
-
278
- // example:
279
- const [getContacts, setContacts] = useContacts.stateControls();
280
-
281
- console.log(getContacts()); // prints the list of contacts
167
+ const [contactsRetriever, contactsApi] = useContacts.stateControls();
168
+ console.log(contactsRetriever()); // Retrieves the current state
282
169
  ```
283
170
 
284
- **stateMutator** is particularly useful when you want to create components that have editing access to a specific store but don't necessarily need to reactively respond to state changes.
285
-
286
- Using the **stateRetriever** and the **stateMutator** allows you to retrieve the state when needed without establishing a reactive relationship with the state changes. This approach provides more flexibility and control over when and how components interact with the global state.
287
-
288
- So, While **useContacts** will allow your components to subscribe to the custom hook, using the **contactsRetriever** method you will be able retrieve the current value of the state. This allows you to access the state whenever necessary, without being reactive to its changes and with the **contactsMutator** you now have the ability to modify the state without the need for subscription to the hook.
289
-
290
- Additionally, to subscribe to state changes, you can pass a callback function as a parameter to the **stateRetriever**. This approach enables you to create a subscription group, allowing you to subscribe to either the entire state or a specific portion of it. When a callback function is provided to the **stateRetriever**, it will return a cleanup function instead of the state. This cleanup function can be used to unsubscribe or clean up the subscription when it is no longer needed.
171
+ #### βœ… Subscribe to changes
291
172
 
292
- ```ts
293
- /**
294
- * This not only allows you to retrieve the current value of the state...
295
- * but also enables you to subscribe to any changes in the state or a portion of it
296
- */
297
- const unsubscribe1 = contactsRetriever((state) => {
298
- console.log('state changed: ', state);
173
+ ```tsx
174
+ const unsubscribe = contactsRetriever((state) => {
175
+ console.log('State updated:', state);
299
176
  });
300
-
301
- const unsubscribe2 = contactsRetriever(
302
- (state) => state.isLoading,
303
- (isLoading) => {
304
- console.log('is loading changed', isLoading);
305
- }
306
- );
307
177
  ```
308
178
 
309
- That's great, isn't it? everything stays synchronized with the original state!!
310
-
311
- ## stateMutator
312
-
313
- Let's add more actions to the state and explore how to use one action from inside another.
314
-
315
- Here's an example of adding multiple actions to the state and utilizing one action within another:
316
-
317
- ```ts
318
- import { createGlobalState } from 'react-hooks-global-states/createGlobalState';
319
-
320
- export const useCount = createGlobalState(0, {
321
- actions: {
322
- log: (currentValue: string) => {
323
- return ({ getState }): void => {
324
- console.log(`Current Value: ${getState()}`);
325
- };
326
- },
327
-
328
- increase(value: number = 1) {
329
- return ({ getState, setState, actions }) => {
330
- setState((count) => count + value);
179
+ #### βœ… Subscriptions are great when one state depends on another.
331
180
 
332
- actions.log(message);
333
- };
334
- },
335
-
336
- decrease(value: number = 1) {
337
- return ({ getState, setState, actions }) => {
338
- setState((count) => count - value);
339
-
340
- actions.log(message);
341
- };
181
+ ```tsx
182
+ const useSelectedContact = createGlobalState(null, {
183
+ callbacks: {
184
+ onInit: ({ setState, getState }) => {
185
+ contactsRetriever(
186
+ (state) => state.contacts,
187
+ (contacts) => {
188
+ if (!contacts.has(getState())) setState(null);
189
+ }
190
+ );
342
191
  },
343
192
  },
344
193
  });
345
194
  ```
346
195
 
347
- Notice that the **StoreTools** will contain a reference to the generated actions API. From there, you'll be able to access all actions from inside another one... the **StoreTools** is generic and allow your to set an interface for getting the typing on the actions.
196
+ ---
348
197
 
349
- # createContext
198
+ ## 🎭 Using Context for Scoped State
350
199
 
351
- **createContext** extends the powerful features of global hooks into the realm of React Context. By integrating global hooks within a context, you bring all the benefits of global state managementβ€”such as modularity, selectors, derived states, and actionsβ€”into a context-specific environment.
200
+ - **Scoped State** – Context state is **isolated inside the provider**.
201
+ - **Same API** – Context supports **selectors, actions, and state controls**.
352
202
 
353
- ## Creating a reusable context
354
-
355
- Forget about the boilerplate of creating a context... with **createContext** it's straightforward and powerful. You can create a context and provider with one line of code.
203
+ ### πŸ“Œ Creating a Context
356
204
 
357
205
  ```tsx
358
- import { createGlobalState } from 'react-hooks-global-states/createContext';
359
-
360
- export const [useCounterContext, CounterProvider] = createContext(2);
206
+ import { createContext } from 'react-global-state-hooks/createContext';
207
+ export const [useCounterContext, CounterProvider] = createContext(0);
361
208
  ```
362
209
 
363
- Then just wrap the components you need with the provider:
210
+ Wrap your app:
364
211
 
365
212
  ```tsx
366
213
  <CounterProvider>
@@ -368,169 +215,108 @@ Then just wrap the components you need with the provider:
368
215
  </CounterProvider>
369
216
  ```
370
217
 
371
- And finally, access the context value with the **useCounterContext**, this function returns a **StateHook**.
372
-
373
- You can execute it immediately to subscribe to the state changes
218
+ Use the context state:
374
219
 
375
220
  ```tsx
376
- const MyComponentInsideTheProvider = () => {
377
- const [count] = useCounterContext()();
378
-
379
- return <>{count}</>;
380
- };
221
+ const [count] = useCounterContext();
381
222
  ```
382
223
 
383
- Or you can retrieve the **useCounterContext.stateControls();** to gain access to the getter and mutator without been affected by the changes on the state
384
-
385
- ```tsx
386
- const MyComponent = () => {
387
- // won't re-render if the counter changes
388
- const [getCount, setCount] = useCounterContext().stateControls();
224
+ ### πŸ“Œ Context Selectors
389
225
 
390
- return <button onClick={() => setCount((count) => count + 1)}>Increase</button>;
391
- };
392
- ```
226
+ Works **just like global state**, but within the provider.
393
227
 
394
- You'll still have selectors to extract just an specific portion of the state. If a selector is added the component only will change if that specific portion of the state changed.
228
+ ---
395
229
 
396
- ```tsx
397
- const MyComponent = () => {
398
- const [isEven, setCount] = useCounterContext()((count) => count % 2 === 0);
230
+ ## πŸ”₯ Observables: Watching State Changes
399
231
 
400
- useEffect(() => {
401
- // lets say that the initial state was *2* and we'll set it now to *4*
402
- setCount(4);
232
+ Observables **let you react to state changes** via subscriptions.
403
233
 
404
- // the component will not re-render cause 4 is also even
405
- }, []);
234
+ ### πŸ“Œ Creating an Observable
406
235
 
407
- return <>{isEven ? 'is even' : 'is odd'}</>;
408
- };
236
+ ```tsx
237
+ export const useCounter = createGlobalState(0);
238
+ export const counterLogs = useCounter.createObservable((count) => `Counter is at ${count}`);
409
239
  ```
410
240
 
411
- **createContext** also allows you to add custom actions to control the manipulation of the state inside the context
241
+ ### πŸ“Œ Subscribing to an Observable
412
242
 
413
243
  ```tsx
414
- import { createContext } from 'react-global-state-hooks/createContext';
415
-
416
- export const [useCounterContext, CounterProvider] = createContext(
417
- { count: 0 },
418
- {
419
- actions: {
420
- increase: (value: number = 1) => {
421
- return ({ setState }) => {
422
- setState((state) => ({
423
- ...state,
424
- count: state.count + value,
425
- }));
426
- };
427
- },
428
- decrease: (value: number = 1) => {
429
- return ({ setState }) => {
430
- setState((state) => ({
431
- ...state,
432
- count: state.count - value,
433
- }));
434
- };
435
- },
436
- },
437
- }
438
- );
244
+ const unsubscribe = counterLogs((message) => {
245
+ console.log(message);
246
+ });
439
247
  ```
440
248
 
441
- And just like with regular global hooks, now instead of a setState function, the hook will return the collection of actions
442
-
443
- Last but not least, you can still creating **selectorHooks** with the **createSelectorHook** function, this hooks will only work if the if they are contained in the scope of the provider.
249
+ ### πŸ“Œ Using Observables Inside Context
444
250
 
445
251
  ```tsx
446
- const useIsEven = useCounterContext.createSelectorHook((count) => count % 2 === 0);
252
+ export const [useStateControls, useObservableBuilder] = useCounterContext.stateControls();
253
+ const createObservable = useObservableBuilder();
254
+ useEffect(() => {
255
+ const unsubscribe = createObservable((count) => {
256
+ console.log(`Updated count: ${count}`);
257
+ });
258
+ return unsubscribe;
259
+ }, []);
447
260
  ```
448
261
 
449
- # Life cycle methods
450
-
451
- There are some lifecycle methods available for use with global hooks, let's review them:
452
-
453
- ```ts
454
- /**
455
- * @description callback function called when the store is initialized
456
- * @returns {void} result - void
457
- * */
458
- onInit?: ({
459
- /**
460
- * Set the metadata
461
- * @param {TMetadata} setter - The metadata or a function that will receive the metadata and return the new metadata
462
- * */
463
- setMetadata: MetadataSetter<TMetadata>;
464
-
465
- /**
466
- * Set the state
467
- * @param {TState} setter - The state or a function that will receive the state and return the new state
468
- * @param {{ forceUpdate?: boolean }} options - Options
469
- * */
470
- setState: StateSetter<TState>;
471
-
472
- /**
473
- * Get the state
474
- * @returns {TState} result - The state
475
- * */
476
- getState: () => TState;
477
-
478
- /**
479
- * Get the metadata
480
- * @returns {TMetadata} result - The metadata
481
- * */
482
- getMetadata: () => TMetadata;
483
-
484
- /**
485
- * Actions of the hook if configuration was provided
486
- */
487
- actions: TActions;
488
- }: StateConfigCallbackParam<TState, TMetadata, TActions>) => void;
489
-
490
- /**
491
- * @description - callback function called every time the state is changed
492
- */
493
- onStateChanged?: (parameters: StateChangesParam<TState, TMetadata, TActions>) => void;
494
-
495
- /**
496
- * callback function called every time a component is subscribed to the store
497
- */
498
- onSubscribed?: (parameters: StateConfigCallbackParam<TState, TMetadata, TActions>) => void;
499
-
500
- /**
501
- * callback function called every time the state is about to change and it allows you to prevent the state change
502
- */
503
- computePreventStateChange?: (parameters: StateChangesParam<TState, TMetadata, TActions>) => boolean;
504
- ```
262
+ ---
263
+
264
+ ## βš–οΈ `createGlobalState` vs. `createContext`
265
+
266
+ | Feature | `createGlobalState` | `createContext` |
267
+ | ---------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
268
+ | **Scope** | Available globally across the entire app | Scoped to the Provider where it’s used |
269
+ | **How to Use** | `const useCount = createGlobalState(0)` | `const [useCountContext, Provider] = createContext(0)` |
270
+ | **createSelectorHook** | `useCount.createSelectorHook` | `useCountContext.createSelectorHook` |
271
+ | **inline selectors?** | βœ… Supported | βœ… Supported |
272
+ | **Custom Actions** | βœ… Supported | βœ… Supported |
273
+ | **Observables** | `useCount.createObservable` | `const [, useObservableBuilder] = useCountContext.stateControls()` |
274
+ | **State Controls** | `useCount.stateControls()` | `const [useStateControls] = useCountContext.stateControls()` |
275
+ | **Best For** | Global app state (auth, settings, cache) | Scoped module state, reusable component state, or state shared between child components without being fully global |
505
276
 
506
- You can pass this callbacks on the config objects when building a **createGlobalState**
277
+ ## πŸ”„ Lifecycle Methods
507
278
 
508
- ```ts
279
+ Global state hooks support lifecycle callbacks for additional control.
280
+
281
+ ```tsx
509
282
  const useData = createGlobalState(
510
283
  { value: 1 },
511
284
  {
512
- metadata: {
513
- someExtraInformation: 'someExtraInformation',
514
- },
515
285
  callbacks: {
516
- // onSubscribed: (StateConfigCallbackParam) => {},
517
- // onInit // etc
286
+ onInit: ({ setState }) => {
287
+ console.log('Store initialized');
288
+ },
289
+ onStateChanged: ({ state, previousState }) => {
290
+ console.log('State changed:', previousState, 'β†’', state);
291
+ },
518
292
  computePreventStateChange: ({ state, previousState }) => {
519
- const prevent = isEqual(state, previousState);
520
-
521
- return prevent;
293
+ return state.value === previousState.value;
522
294
  },
523
295
  },
524
296
  }
525
297
  );
526
298
  ```
527
299
 
528
- Finally, if you have a very specific necessity but still want to use the global hooks, you can extend the **GlobalStoreAbstract** class.
300
+ Use **`onInit`** for setup, **`onStateChanged`** to listen to updates, and **`computePreventStateChange`** to prevent unnecessary updates.
301
+
302
+ ## Metadata
303
+
304
+ There is a possibility to add non reactive information in the global state:
305
+
306
+ ```tsx
307
+ const useCount = createGlobalState(0, { metadata: { renders: 0 } });
308
+ ```
309
+
310
+ How to use it?
529
311
 
530
- Let's see an example again with the **asyncStorage** custom global hook but with the abstract class.
312
+ ```tsx
313
+ const [count, , metadata] = useCount();
531
314
 
532
- ```ts
533
- extends GlobalStoreAbstract<State, Metadata, ActionsConfig>
315
+ metadata.renders += 1;
534
316
  ```
535
317
 
536
- # That's it for now!! hope you enjoy coding!!
318
+ ## 🎯 Ready to Try It?
319
+
320
+ πŸ“¦ **NPM Package:** [react-hooks-global-states](https://www.npmjs.com/package/react-hooks-global-states)
321
+
322
+ πŸš€ Simplify your **global state management** in React & React Native today! πŸš€
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-hooks-global-states",
3
- "version": "7.0.10",
4
- "description": "This is a package to easily handling global-state across your react-components No-redux",
3
+ "version": "7.0.12",
4
+ "description": "This is a package to easily handling global-state across your react-components using hooks.",
5
5
  "main": "./bundle.js",
6
6
  "types": "./index.d.ts",
7
7
  "sideEffects": false,
@@ -103,6 +103,7 @@
103
103
  "url": "git+https://github.com/johnny-quesada-developer/react-hooks-global-states.git"
104
104
  },
105
105
  "keywords": [
106
+ "preact",
106
107
  "react",
107
108
  "redux",
108
109
  "state",
@@ -111,8 +112,7 @@
111
112
  "global-state",
112
113
  "context",
113
114
  "typescript",
114
- "react-native",
115
- "async-storage"
115
+ "react-native"
116
116
  ],
117
117
  "author": "Johnny Quesada",
118
118
  "license": "MIT",